hdu 4468 spy 极其精彩的一道kmp灵活运用题
出的超級好的一道題。至于好在哪里,請思考題目:
題意抽象出來為給定一個字符串r,找出它的一個最短后綴s,使得這個r可以被 s的某前綴+s的某前綴+......+s的某前綴+s本身構造出來。
具體題目描述如下:
“Be subtle! Be subtle! And use your spies for every kind of business. ”?
— Sun Tzu?
“A spy with insufficient ability really sucks”?
— An anonymous general who lost the war?
You, a general, following Sun Tzu’s instruction, make heavy use of spies and agents to gain information secretly in order to win the war (and return home to get married, what a flag you set up). However, the so-called “secret message” brought back by your spy, is in fact encrypted, forcing yourself into making deep study of message encryption employed by your enemy.?
Finally you found how your enemy encrypts message. The original message, namely s, consists of lowercase Latin alphabets. Then the following steps would be taken:?
* Step 1: Let r = s?
* Step 2: Remove r’s suffix (may be empty) whose length is less than length of s and append s to r. More precisely, firstly donate r[1...n], s[1...m], then an integer i is chosen, satisfying i ≤ n, n - i < m, and we make our new r = r[1...i] + s[1...m]. This step might be taken for several times or not be taken at all.?
What your spy brought back is the encrypted message r, you should solve for the minimal possible length of s (which is enough for your tactical actions).
輸入:
There are several test cases.?
For each test case there is a single line containing only one string r (The length of r does not exceed 10?5). You may assume that the input contains no more than 2 × 10?6?characters.?
Input is terminated by EOF.
輸出:
For each test case, output one line “Case X: Y” where X is the test case number (starting from 1) and Y is the desired answer.
樣例:
abc aab abcadabcabcad aaabbbaaaabbbaa abcababcd樣例答案: Case 1: 3 Case 2: 2 Case 3: 5 Case 4: 6 Case 5: 4
題解:
這題初看很容易只能想到枚舉答案長度i,從r后面截長度為i的后綴然后暴力匹配是否滿足條件。復雜度顯然太高無法接受。
之后如果運用動態規劃的思想,可以對r[1...k]考慮子問題。
但是會發現由于要加一個s本身,考慮完全同質的子問題有點問題。
于是考慮r[1...k]可以表示為s1某前綴+s1某前綴+...+s1某前綴時s1的最短長度
那么對于r[1...k+1],
如果r[k+1]可以成為s1的某前綴的一部分或者自己成為s1的前綴。s1就仍然滿足r[1....k+1]的要求,最短長度不變;
如果不可以,那么理論上把s1的末尾添加字符r[k+1]便滿足了r[1...k+1]的要求。但是這時得考慮下最后得有一個s本身的問題。
所以添加字符的時候不能只加r[k+1],應該加一段字符。從r[1...k+1]中上一次模板串跟r[1..k+1]的一部分完全匹配的地方開始加,加到r[k+1]。
一直掃描完題意給的整個r。最后再給一直維護的模板串加一段:最后一次完全匹配的地方到r的末尾的字符。
以上是思考過程。
以下是更加嚴謹的題解敘述:
從頭開始掃描r,始終維護一個模板串s和上一次完全匹配位置的標記mark:
在r[i]處有三種操作:
1.若在r[i]處成功進行kmp匹配,則模板串不變
2.如匹配失敗,動態添加模板串,添加內容為位置為mark至i的子串
3.如果進行了一次完全匹配,更新mark。
1 #include<cstdio> 2 #include<cstring> 3 #define rep(i,a,b) for(int i=a;i<=b;++i) 4 using namespace std; 5 const int MAXN=110000; 6 char s[MAXN]; 7 char ans[MAXN]; 8 int next[MAXN]; 9 int tot; 10 int main() 11 { 12 //freopen("in.txt","r",stdin); 13 int cnt=0; 14 while(scanf("%s",s)!=EOF) 15 { 16 int len=strlen(s); 17 tot=0; 18 ans[tot++]=s[0]; //將維護的模板串初始化 19 next[0]=0; 20 int mark=0; //mark標記上一次完全匹配的位置 21 for(int i=0,k=0;i<len;++i) 22 { 23 while(k>0&&s[i]!=ans[k]) //嘗試s[i]是否能成為維護的模板串前綴的一部分 24 { 25 k=next[k-1]; 26 } 27 if(s[i]==ans[k]) k++; 28 else if(k==0) //嘗試失敗 29 { 30 for(int j=mark+1;j<=i;++j) //更新模板串 31 { 32 ans[tot++]=s[j]; 33 int tmp=next[tot-2]; //模板串的next數組需要跟著動態更新 34 while(tmp>0&&ans[tmp]!=ans[tot-1]) tmp=next[tmp-1]; 35 if(ans[tmp]==ans[tot-1]) tmp++; 36 next[tot-1]=tmp; 37 } 38 mark=i; 39 } 40 if(k==tot) mark=i,k=next[tot-1]; //進行了一次完全匹配,更新mark,并將k跳回 41 } 42 for(int j=mark+1;j<len;++j) ans[tot++]=s[j]; 43 printf("Case %d: %d\n",++cnt,tot); 44 } 45 return 0; 46 }
?
做出來之后發現這道題考察到了kmp算法的所有操作。但是需要人將其kmp算法的各個操作有機地拆開與重組,來解決這個新的問題。非常有助于加深對kmp的理解。
轉載于:https://www.cnblogs.com/zhixingr/p/7630181.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的hdu 4468 spy 极其精彩的一道kmp灵活运用题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实验任务四:登录界面、实验任务五:猜数字
- 下一篇: 初识Hibernate之关联映射(一)