【算法課】字典序問題


題目描述

  在數據加密和數據壓縮中常需要對特殊的字符串進行編碼。給定的字母表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 }
對拍函數

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM