《计算机算法设计与分析》中第一章的第二道题-字典序问题。
题目如下:
在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A由26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。
1 2 ... 26 27 28 ...
a b ... z ab ac ...
样例输入:
2
a
b
样例输出:
1
2
此题是一个排列组合中组合的一个应用,也就是从26个字母中任取n个字母形成一个组合,对组合来说,也就是两种排列相结合形成的,此题中第一种排列就是从26个字母中任取n个字母的排列,一共有26!/(26-n)!种;第二种排列也就是n个字母之间的排列,一共有n!种,第一种/第二种就是26!/(26-n)1*n! 种,见下图详解:
此方法就是本题的关键所在,看懂此方法后此题就很简单了。
解题步骤:我们假设输入的字符长度为len,那么我们可以先找到长度小于len的升序排序,也就是按照上面的方法将字符长度为1到长度为len-1的升序排序的数量先求出来(第一步);接下来再处理长度为len的个数就可以了(第二步)。
对于长度为len的字符串,从不是以字符串首字母开头开始找,我们从字母a为开头的字符串开始找,先确定第一个字母在确定第二个字母,一直到全部确定,将这些相加就可以了。
详细步骤:由于字符串是升序的
1、所以从第一个字母从a开始找,如果第一个字母不是a,则从剩下的26-1个字母里面任取n-1个字母,进行组合,(方法就和前面方法的原理是一样的)得出的值与前面长度从1到len-1的编码值相加;
2、继续从字母b开始找,如果字母b也不是,根据第一步一样,从26-2个字母中任取n-2个字母,进行组合,得出的值与前面得出的编码值相加;
3、假如字母c为第一个字母,则我们可以开始找第二个字母,第二个字母从d开始找,也就是从前一个字母的后面的第一个字母继续找,和上面前两步一样操作,
直到找到最后一个字母,此时的编码值是此字符串按照顺序的前一个字符串的编码值,在加1就是此字符串的编码值。
举例:cegi=(26+325+2600)+(2300+2024)+(231)+(20)+(1)=7258
len=4;
第一步:确定长度1到3的组合总数
C(26,1)+C(26,2)+C(26,3)
=26+325+2600
第二步:分别求出c,e,g,i前有多少种组合(注意是升序字典,每次找都要从前一位字母+1开始)
第一位c,,字符串长度为4,待选n=3位(第一位是确定的),前有从abcd->a***->axyz,总数m=25(a开始,b到z可选),即C(25,3)=2300,从bcde->a***->bxyz,总数m=24(b开始,c到z可选),即C(24,3)=2024;
第二位e,字符串长度为4,待选n=2位(第一,二位是确定的),前有从cdef->cd**->cdyz,总数m=22(d开始,e到z可选),即C(22,2)=231;
但三,四位和第二位类似,以此类推可得到C(20,1)=20和C(18,0)=1。只有第一位是特殊情况,其没有前一位,即m永远为25开始(a开始,b到z可选)
代码如下所示:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
int fun(int m,int n){//求排列组合
int max=1,min=1;
for(int i=m;i>m-n;i--){
max*=i;
}
for(int i=n;i>0;i--){
min*=i;
}
printf("+%d\n",max/min);
return max/min;
// 在m个中选出n个进行无序排列 的总排列数
}
int main()
{
int n;
scanf("%d",&n);
int f=0;
while(n){
char str[7];
scanf("\n%s",&str);
int len=strlen(str);
int ans=1;
for(int i=1;i<len;i++){
ans+=fun(26,i);
}
printf("\n");
int c[len]={str[0]-'a',};
int d[len]={25,};
for(int i=1;i<len;i++){
c[i]=str[i]-str[i-1]-1;//循环次数
d[i]='z'-str[i]+1;//组合起始总数
}
for(int i=0;i<len;i++){
printf("a[%d]循环次数=%d 组合起始总数=%d\n",i,c[i],d[i]);
}
for(int i=0;i<len;i++){
for(int j=0;j<c[i];j++){
ans+=fun(d[i]-j,len-i-1);
}
printf("\n");
}
printf("%d\n",ans);
n--;
}
return 0;
}
![运行参考](https://i-blog.csdnimg.cn/blog_migrate/f8af25cb6f66bee77c073af8cf5432cb.png)