2-1字典序问题 算法

2-1字典序问题

问题描述:

在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。

a

b

c

ab

ac

1

2

3

27

28

样例输入:

 2

 a

 b

样例输出:

 1

 2 

算法:

/*2-2 字典序问题 递归调用 
分析:给定一个k位字符串需要分两步计算:
1、计算所有<k位字符串的总和
2、计算=k位,但是编码小于他的总和
令f(i,k)表示以第i个字符打头长度为k的字符串总个数
g(k)表示长度为k的所有字符串总个数
有:特殊情况f(i,1)=1
 其他情况f(i,k)= 累加f(j,k-1) ,j从i+1到26
 g(k)=累加f(i,k)=累加i(累加j f(j,k-1)),i=1 ~ 26,j=i+1 ~ 26 
*/ 
#include <iostream>
#include <string> 
using namespace std;
 
 int f(int i,int k){
 	int sum=0;
 	if(k==1)
 	    return 1;
 	else{
 		for(int j=i+1;j<=26;j++){//f(i,k)= 累加f(j,k-1) ,j从i+1到26 
		 //例如x1x2x3x4 以x1开头长度为4的字符串和以x2开头的三位字符串之间差了以x1后面字符到字符z开头的四位字符串 
 			sum+=f(j,k-1);
		 }
		 return sum;
	 }
 } 
 
 int g(int k){
 	int sum=0;
 	for(int i=1;i<=26;i++){
 		sum+=f(i,k);
	 }
	 return sum;
 }
 
 int calulate(string s){
 	int sum=0;
 	int k=s.length();
 	for(int i=1;i<k;i++){//第一步:计算所有小于k位的字符串的总和 
 		sum+=g(i); 
	 }
	 
	 int h=s[0]-'a'+1;//计算s中第一个字母 是26字符中的第几个字符 
	 
	 for(int i=1;i<h;i++){//第二步:计算以 h字符之前的字符 打头长度为k的字符串个数并加到sum上 
	 sum+=f(i,k); 
	 }
	  
	 for(int i=1,temp=h;i<k;i++){//第三步:计算以h字符开头,剩下的所有的字符串个数 
	 	int n=s[i]-'a'+1;//n为当前首位字母为26位字符中的第几个 
	 	int length=k-i;//获取当前字符串长度
		 for(int j=temp+1;j<n;j++){//计算 从前一个首字母的后一位 到 当前首字母位置  包含的所有字符串个数并加到sum上 
		 	sum+=f(j,length);
		 	temp=n;
		 } 
	 } 
	  return sum+1;//最后加上原字符串 
 }
int main(int argc, char** argv) {
	int n;
	string s;
	cin>>n;
	for(int i=0;i<n;i++){ 
    cin>>s;  
    cout<<calulate(s);  	
	}
	return 0;
}
点赞