搜索和组合数学P1246 编码
题意简述
- 要求给字符串按照规定编号,字符串长度小于等于 \(6\) 。
- 能编号的字符串的字母一定是递增的,规定:
-
\(\mathrm{a}\rightarrow 1\)
-
\(\mathrm{b}\rightarrow 2\)
\(\cdots\)
-
\(\mathrm{z}\rightarrow 26\)
-
\(\mathrm{ab}\rightarrow 27\)
-
最后一个编号 \(\mathrm{uvwxyz}\rightarrow313911\)
-
- 如果给出的字符串字母不递增,则输出 \(0\) ,否则输出对应编号。
暴力枚举
首先数据范围很小。先简单算一下,用组合数计算出每一个长度能编号的字符串数目。
- 长度为 \(1\) 能编号的字符串数目为 \(C_{26}^1 = 26\)
- 长度为 \(2\) 能编号的字符串数目为 \(C_{26}^2 = 325\)
把 \(6\) 个组合数加起来编号就是 \(313911\) ,即最后一个字符串 \(\mathrm{uvwxyz}\) 的编号,完全可以枚举每一个可以编号的字符串,找到匹配的就输出编号即可,否则输出 \(0\)。
\(\mathrm{Code}\):
#include <bits/stdc++.h>
#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\)~ \(n-1\) 的能编号字符串的总数,最后再解决长度为 \(n\) 的字符串情况。涉及的细节可能比较多,我尽可能注释了。
\(\mathrm{Code}\):
#include <bits/stdc++.h>
#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;
}
还有问题的可以评论👻。
想做一个高质量的博客◐▽◑