搜索和组合数学P1246 编码

  题目链接P1246 编码

博客园食用更佳

题意简述

  • 要求给字符串按照规定编号,字符串长度小于等于 6 6 6
  • 能编号的字符串的字母一定是递增的,规定:
    • a → 1 \mathbf{a}\rightarrow 1 a1

    • b → 2 \mathbf{b}\rightarrow 2 b2

      ⋯ \cdots

    • z → 26 \mathbf{z}\rightarrow 26 z26

    • a b → 27 \mathbf{ab}\rightarrow 27 ab27

    • 最后一个编号 u v w x y z → 313911 \mathbf{uvwxyz}\rightarrow313911 uvwxyz313911

  • 如果给出的字符串字母不递增,则输出 0 0 0 ,否则输出对应编号。

暴力枚举

  首先数据范围很小。先简单算一下,用组合数计算出每一个长度能编号的字符串数目。

  • 长度为 1 1 1 能编号的字符串数目为 C 26 1 = 26 C_{26}^1 = 26 C261=26
  • 长度为 2 2 2 能编号的字符串数目为 C 26 2 = 325 C_{26}^2 = 325 C262=325

  把 6 6 6 个组合数加起来编号就是 313911 313911 313911 ,即最后一个字符串 u v w x y z \mathbf{uvwxyz} uvwxyz 的编号,完全可以枚举每一个可以编号的字符串,找到匹配的就输出编号即可,否则输出 0 0 0

C o d e : Code: Code:

#include 
#define FOR(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
char s[10],test[10];//s记录询问的字符串,test记录尝试的字符串
int n,id;  //n记录询问字符串长度,id为编号

//last记录前一个字母,step记录递归层数,枚举下一位
void dfs(char last, int step){
     
    if(!step) return;
    for (char i = last + 1; i <= 'z'; i++) {
     
        test[step] = i;
        if(step == 1) {
     
            id++;     //枚举到最后一位编号才加一
            int flag = 1;
            FOR(j,1,n) if(s[j] != test[n - j + 1]) flag = 0; //逐位比对,注意test是逆着记录的
            if(flag) {
     cout << id;exit(0);}
        }
        dfs(i,step-1);
    }
}

signed main(){
     
    scanf("%s", s + 1);
    n = strlen(s + 1);
    FOR(i,1,n)
        dfs('a'-1,i);

    cout<<0;  //在dfs中未找到,说明字符串不满足要求
    return 0;
}

组合数学

  这题如果数据量较大可以用组合数学解决。我们先算出长度为 1 1 1~ n − 1 n-1 n1 的能编号字符串的总数,最后再解决长度为 n n n 的字符串情况。涉及的细节可能比较多,我尽可能注释了。

C o d e : Code: Code:

#include 
#define FOR(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
char s[10];//ch记录询问的字符串
int n,id,c[30][30];  //n记录询问字符串长度

signed main(){
     
    FOR(i,0,26) c[i][0] = 1;
    FOR(i,1,26) FOR(j,1,26) c[i][j] = c[i-1][j]+c[i-1][j-1];//组合数递推式,
    scanf("%s", s + 1);
    n = strlen(s + 1);
    //处理长度小于n的字符串编号
    FOR(i,1,n-1) {
     
        if(s[i] >= s[i+1]) {
     
            cout<<0;//不升序排列就直接输出0
            exit(0);
        }
        id += c[26][i]; //从26个字母中选i个,一定可以排成递增的字符串
    }
    int j ;
    FOR(i,1,n){
     
        //j要分情况赋值,前i-1位已固定为s[1]到s[i-1](i != 1)
        if(i == 1) j = 'a';
        else j = s[i-1]+1;

        for(;j < s[i];j++)
            id += c['z'-j][n-i]; //在第i位为字母j的情况下,后面n-i位的组合情况。
    }
    cout<<id+1; //记得加一
    return 0;
}

  还有问题的可以评论。

你可能感兴趣的:(搜索)