【算法课】字典序问题
题目描述
在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A由26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。
1 |
2 |
3 |
… |
ab |
ac |
a |
b |
c |
27 |
28 |
输入
第一行一个整数T,表示测试组数(1<=T<=1e6)。
接下来T行,每行一个长度不超过6的升序字符串(仅含小写字母)。
接下来T行,每行一个长度不超过6的升序字符串(仅含小写字母)。
输出
输出T行,每行一个整数代表答案。
样例输入
2
a
b
样例输出
1
2
【题意】
其实这个题意是看这个列表看出来的,不重复的字母 按字典顺序给出,最大长度不超过6.
和我们字典有点不同
1、长度从小到大
2、不存在重复的情况。
多亏老师的提醒才能做出来,不然会在WA的路上越走越远。
预处理所有f(i,j) 长度为i,以j字母开头的所有情况出来。
预处理注意:1、不重复 ,2、长度占据一些位置,不能直接以下一个字母到z。
求解答案时,注意如果当前是前一个字母的下一个位置不需要累加答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 30; 6 7 int Sum_Len[N]; 8 int Pre_Len[N]; 9 int f[8][N]; 10 char Str[N]; 11 12 // 预处理出所有f[i][j] 13 // f[i][j] 长度为i,j开头的所有情况 14 void Init(){ 15 16 //长度为1时所有情况就是 1 17 for(int i=0;i<27;i++) f[1][i] = 1 ; 18 19 //长度在递增时,转移方程为:f[len][i] += f[len-1][ i+1 ………z] 20 //注意一点的是,因为长度的限制,所以无法枚举到最后一个字符。 21 //f[len][i] += f[len-1][ i+1 ……… ('z' - len - 1) ] 22 for( int Len = 2 ; Len <= 6 ; Len ++ ){ 23 for( int i=0 ; i < 26 ; i++ ){ 24 for( int j=i+1 ; j <= 26 - Len + 1 ; j++ ){ 25 f[Len][i] += f[Len-1][j]; 26 } 27 } 28 } 29 30 for(int i=1;i<=6;i++){ 31 for(int j=0;j<26;j++){ 32 Sum_Len[i] += f[i][j]; 33 } 34 //记录在统计长度为i时所有情况。 35 Pre_Len[i] = Pre_Len[i-1] + Sum_Len[i]; 36 } 37 38 } 39 40 int main() 41 { 42 Init(); 43 int T; 44 scanf("%d",&T); 45 while(T--){ 46 scanf("%s",Str+1); 47 int len = strlen( Str+1 ) ; 48 int Ans = Pre_Len[len-1] + 1 ; 49 50 //预处理一个位置出来. 51 Str[0] = 'a' - 1 ; 52 for( int i = 1 , Len = len ; i <= len ; i++ ,Len-- ){ 53 //如果是连着的情况就不统计,例如abc 54 55 if( Str[i-1] + 1 == Str[i] ) continue ; 56 57 //如果不是连着的需要把对应的位置进行累加,注意累加的起点. 58 //如 ab(e) -> abc,abd 59 for( int j = Str[i-1] - 'a' + 1 ; j < Str[i]-'a' ; j++ ){ 60 Ans += f[Len][j]; 61 } 62 } 63 printf("%d\n",Ans); 64 } 65 return 0 ; 66 } 67 68 69 70 /* 71 72 a 73 1 74 75 ab 76 27 77 78 yz 79 351 80 81 abc 82 352 83 84 bcd 85 652 86 87 */
【回溯的做法】
就当练习dfs了,超时是正常的,因为搜索空间太大了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #pragma GCC optimize(2) 2 #pragma GCC optimize(3) 3 #include<unordered_map> 4 #include<iostream> 5 #include<cstdio> 6 #include<map> 7 using namespace std; 8 unordered_map< string , int > Mp; 9 int Len , No ; 10 char character[27] ; 11 12 inline void dfs( int pos , int len , string s ){ 13 if( (26 - pos) + len < Len ) return ; 14 if( len == Len ){ 15 Mp[ s ] = ++No ; 16 //cout << s << endl; 17 return ; 18 } 19 //cout << " ##### "<< endl; 20 if( pos == 26 ) return ; 21 for( register int i = pos ; i < 26 ; i++ ){ 22 //cout << i << " ### " << endl; 23 dfs( i + 1 , len + 1 , s+string(character+i,1) ); 24 } 25 26 } 27 int main() 28 { 29 30 for(int i=0;i<26;i++){ 31 character[i] = i+'a'; 32 } 33 34 int T; 35 for(Len = 1 ; Len <= 6 ; Len++ ) 36 dfs( 0 , 0 , "" ); 37 38 //cout << No << endl; 39 40 //freopen("input.txt","r",stdin); 41 //freopen("out2.txt","w",stdout); 42 ios_base :: sync_with_stdio(false ); 43 cin.tie(NULL) , cout.tie(NULL); 44 cin >> T; 45 while(T--){ 46 string str ; 47 cin >> str; 48 cout << Mp[str] << endl; 49 } 50 return 0 ; 51 } 52 /* 53 54 26 * 25 * 24 * 23 * 22 *21 55 165765600 56 57 */
【对拍版】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 30; 6 7 int Sum_Len[N]; 8 int Pre_Len[N]; 9 int f[8][N]; 10 char Str[N]; 11 12 // 预处理出所有f[i][j] 13 // f[i][j] 长度为i,j开头的所有情况 14 void Init(){ 15 16 //长度为1时所有情况就是 1 17 for(int i=0;i<27;i++) f[1][i] = 1 ; 18 19 //长度在递增时,转移方程为:f[len][i] += f[len-1][ i+1 ………z] 20 //注意一点的是,因为长度的限制,所以无法枚举到最后一个字符。 21 //f[len][i] += f[len-1][ i+1 ……… ('z' - len - 1) ] 22 for( int Len = 2 ; Len <= 6 ; Len ++ ){ 23 for( int i=0 ; i < 26 ; i++ ){ 24 for( int j=i+1 ; j <= 26 - Len + 1 ; j++ ){ 25 f[Len][i] += f[Len-1][j]; 26 } 27 } 28 } 29 30 for(int i=1;i<=6;i++){ 31 for(int j=0;j<26;j++){ 32 Sum_Len[i] += f[i][j]; 33 } 34 //记录在统计长度为i时所有情况。 35 Pre_Len[i] = Pre_Len[i-1] + Sum_Len[i]; 36 } 37 38 } 39 40 int main() 41 { 42 //freopen("input.txt","r",stdin); 43 //freopen("out1.txt","w",stdout); 44 Init(); 45 int T; 46 scanf("%d",&T); 47 while(T--){ 48 scanf("%s",Str+1); 49 int len = strlen( Str+1 ) ; 50 int Ans = Pre_Len[len-1] + 1 ; 51 52 //预处理一个位置出来. 53 Str[0] = 'a' - 1 ; 54 for( int i = 1 , Len = len ; i <= len ; i++ ,Len-- ){ 55 //如果是连着的情况就不统计,例如abc 56 57 if( Str[i-1] + 1 == Str[i] ) continue ; 58 59 //如果不是连着的需要把对应的位置进行累加,注意累加的起点. 60 //如 ab(e) -> abc,abd 61 for( int j = Str[i-1] - 'a' + 1 ; j < Str[i]-'a' ; j++ ){ 62 Ans += f[Len][j]; 63 } 64 } 65 printf("%d\n",Ans); 66 } 67 return 0 ; 68 } 69 70 71 72 /* 73 74 a 75 1 76 77 ab 78 27 79 80 yz 81 351 82 83 abc 84 352 85 86 bcd 87 652 88 89 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 freopen("input.txt","w",stdout); 6 int T; 7 T = 10 ; 8 printf("%d\n",T); 9 for(int i=0;i<T;i++){ 10 int n = rand()%6+1; 11 int a[30]={0}; 12 for(int i=0;i<26;i++){ 13 a[i] = i; 14 } 15 random_shuffle(a,a+26); 16 sort(a,a+n); 17 for(int i=0;i<n;i++){ 18 printf("%c",a[i]+'a'); 19 } 20 puts(""); 21 } 22 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 5 while(1){ 6 system("./造数据_字典序"); 7 system("./回溯版_字典序"); 8 system("./规律版_字典序"); 9 if(system("diff out1.txt out2.txt")) break; 10 } 11 return 0; 12 }