Problem description
编码工作常被运用于密文或压缩传输。这里我们用一种最简单的编码方式进行编码:把一些有规律的单词编成数宇。
字母表中共有26个字母{a,b,…,z},这些特殊的单词长度不超过6且字母按升序排列。把所有这样的单词放在一起,按字典顺序排列,一个单词的编码就对应着它在字典中的位置。
例如:
a→1
b→2
……
z→26
ab→27
ac→28
……
你的任务就是对于所给的单词,求出它的编码。
Input format
仅一行,被编码的单词。
Output format
仅一行,对应的编码。如果单词不在字母表中,输出0。
Algorithm design
模拟AC/动态规划AC
Problem analysis
模拟AC
先敲一下暴力,方便对拍
模拟的思路很好想,将字母转化为数字,进行类似26进制的依次加法
(发现好像直接用字母处理更方便..)
但26进制应该z过了之后是aa这样,所以还是有所不同
无论如何,到底只是一个简单的维护单调序列的问题
每次从后往前枚举,找到第1个可以进的数字,加1
同时将其后所有数字变为从它开始的单调递增1序列
(如果是26进制则应该将之后所有数字变为a)
当然在找数字的时候也要注意保证修改后不会有数超过z
如果找不到这样的数字,那么加1位,从头开始abc…
每次将得到的结果与目标进行比对即可
至于不在字母表中,直接在开头判断,出现非单调性,或者不在a~z就输0
复杂度不高,O(26个数中长度不大于6的有序区间的个数*6),不会算…
可以过,但是数据模拟到6位的时候,程序会爆掉,不知道为什么
动态规划AC
在随手画画的过程中发现
以字母j开头的长为i的字母的总数是一个可以算出的值
比如说,3,w就是3,x+2,x,其他的以此类推
Opt[i][j]=opt[i][j+1]+opt[i-1][j+1]
为什么呢
在同一长度下,i总是包括i+1的所有情况(只是开头字母换了)
又因为i比i+1少1,所以第二个字母又比i+1的第二个字母多1种情况
故在第二个字母为i+1时,opt[i][j]又有一个opt[i-1][j+1]
那么所有的opt[i][j]都是能算出来的
接着就是数学处理了
为了方便,先将小于所给单词的所有情况都加上
然后每次取出位数相同,首数却小于自身的情况,再删去自己的首数继续
这里有个难点,每次开始枚举的起点,都应该是上一次的首数+1
其他没什么了
Source code
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 struct node 6 { 7 int num[7]; 8 int len; 9 }ans,key;//定义结构体,存储数字和规格 10 string st; 11 12 void input() 13 { 14 freopen("test.in","r",stdin); 15 freopen("test.out","w",stdout); 16 cin>>st; 17 memset(key.num,0,sizeof(key.num)); 18 memset(ans.num,0,sizeof(ans.num)); 19 key.len=1; 20 key.num[1]=1; 21 ans.len=st.size(); 22 for(int i=1;i<=ans.len;i++) 23 ans.num[i]=st[i-1]-'a'+1; 24 //将答案保存入结构体 25 return ; 26 } 27 28 void operate(node now,int turn) 29 { 30 if(now.len==ans.len) 31 { 32 bool flag=true; 33 for(int i=1;i<=now.len;i++) 34 if(now.num[i]!=ans.num[i]) 35 { 36 flag=false; 37 break; 38 } 39 if(flag) 40 { 41 cout<<turn<<endl; 42 return; 43 } 44 }//满足答案即输出 45 for(int i=now.len;i>=1;i--) 46 if(now.num[i]+now.len-i<26) 47 { 48 now.num[i]++; 49 for(int j=i+1;j<=now.len;j++) 50 now.num[j]=now.num[j-1]+1; 51 operate(now,turn+1); 52 return ; 53 } 54 now.len++; 55 for(int i=1;i<=now.len;i++) 56 now.num[i]=i; 57 operate(now,turn+1); 58 //和Analysis说的一样了 59 return ; 60 } 61 62 int main() 63 { 64 input(); 65 for(int i=1;i<=ans.len;i++) 66 if((i>1&&ans.num[i]<=ans.num[i-1])||ans.num[i]>26||ans.num[i]<1) 67 { 68 cout<<"0"<<endl; 69 return 0; 70 } 71 //直接判断是否不符合 72 operate(key,1); 73 return 0; 74 }
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int cnt[10][30],ans,len; 6 //cnt[i][j]表以i开头的j位数有多少个 7 string st; 8 9 void input() 10 { 11 freopen("test.in","r",stdin); 12 freopen("test.out","w",stdout); 13 cin>>st; 14 len=st.size(); 15 return ; 16 } 17 18 void pretreatment() 19 { 20 memset(cnt,0,sizeof(cnt)); 21 for(int i=1;i<=26;i++) 22 cnt[1][i]=1; 23 //1位的任何字母都只有一种 24 for(int i=2;i<=6;i++) 25 for(int j=26-i+1;j>=1;j--) 26 cnt[i][j]=cnt[i][j+1]+cnt[i-1][j+1]; 27 //从当前位数下可以采用的最大数首开始递减,如:w,3比x,3多了x,2种情况 28 return ; 29 } 30 31 void operate() 32 { 33 ans=1; 34 for(int i=1;i<len;i++) 35 for(int j=1;j<=26-i+1;j++) 36 ans+=cnt[i][j]; 37 //先把比自己位数小的算上,可以用前缀和优化,但是可以过就不费事了 38 int wei=1; 39 while(len) 40 { 41 for(int i=wei;i<st[0]-'a'+1;i++) 42 ans+=cnt[len][i]; 43 //把同位数中低于自己的算上 44 wei=st[0]-'a'+2; 45 st=st.substr(1); 46 len--; 47 } 48 return ; 49 } 50 51 int main() 52 { 53 input(); 54 for(int i=0;i<len;i++) 55 if((i>0&&st[i]<=st[i-1])||st[i]>'z'||st[i]<'a') 56 { 57 cout<<"0"<<endl; 58 return 0; 59 } 60 //先判断是不是符合单词 61 pretreatment(); 62 operate(); 63 cout<<ans<<endl; 64 return 0; 65 }
over