本题为组合数学题目,易知长度为n的串的个数就是从26个字母表中选出n个字母的个数,只不过排列方式必须升序。
所以若题目给出一个长度为L的串,可以直接对其打表计算出长度1到L-1的串的个数。
然后再计算出长度为L且字典序小于给定串的个数,易知满足要求的头字母有[1,s[1]](此处已编码),已知只要头字母为[1,s[1]-1],剩下的L-1位只要升序都满足要求。假设x在上述集合中,头字母为x的个数为nchoosek(26-x,L-1)。接下来的可以依次类推。
#include<cstdio>
#include<cstring>
#define maxL 12
int nchoosek(int n,int k)
{
if(k == 0) return 1;
long long x,y;
x = y = 1;
while((x *= n)&&(y *= k)&&(--k)&&(--n));
return x/y;
}
int main()
{
int i,j;
int L;
char IsCode;
int num;
int sum[10];
int baseTab[9] = {26,325,2600,14950,65780,230230,657800,1562275,3124550};
sum[0] = 0;
for(i = 1;i <= 9;i++)
sum[i] = sum[i-1]+baseTab[i-1];
char s[maxL];
while(~scanf("%s",s+1))
{
s[0] = 'a'-1;
IsCode = true;
num = 0;
L = strlen(s)-1;
num += sum[L-1];
for(i = 0;i <= L;i++)
{
s[i] -= 'a'-1;
if(i != 0&&s[i] <= s[i-1])
{
printf("0\n");
IsCode = false;
break;
}
}
if(!IsCode) continue;
for(i = 1;i <= L;i++)
for(j = s[i-1]+1;j <= s[i]-1;j++)
num += nchoosek(26-j,L-i);
printf("%d\n",num+1);
}
return 0;
}