【算法课】字典序问题

题目描述

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

… 

ab 

ac 

 

27 

28 

 

输入

第一行一个整数T,表示测试组数(1<=T<=1e6)。
接下来T行,每行一个长度不超过6的升序字符串(仅含小写字母)。 

 

输出

输出T行,每行一个整数代表答案。

 

样例输入

2
a
b

 

样例输出

1
2

 

 

【题意】

  其实这个题意是看这个列表看出来的,不重复的字母 按字典顺序给出,最大长度不超过6.

  和我们字典有点不同

  1、长度从小到大

  2、不存在重复的情况。

  

  多亏老师的提醒才能做出来,不然会在WA的路上越走越远。

  预处理所有f(i,j)  长度为i,以j字母开头的所有情况出来。

  

  预处理注意:1、不重复 ,2、长度占据一些位置,不能直接以下一个字母到z。

  

  求解答案时,注意如果当前是前一个字母的下一个位置不需要累加答案。

 

 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 */
View Code

 


 

 

【回溯的做法】

  就当练习dfs了,超时是正常的,因为搜索空间太大了。

 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 */
爆搜写法

 

【对拍版】

 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 */
正解

 

 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 }
造数据

 

 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 }
对拍函数

 

posted @ 2019-08-30 22:30  Osea  阅读(1391)  评论(0编辑  收藏  举报