hdu 4468 spy 极其精彩的一道kmp灵活运用题

Posted 缄默火

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的理解。

以上是关于hdu 4468 spy 极其精彩的一道kmp灵活运用题的主要内容,如果未能解决你的问题,请参考以下文章

HDU 6153 KMP

hdu 2087剪花布条 kmp模板题

[KMP]HDU2087 剪花布条

[HDU3336]Count the string(KMP+DP)

hdu 1358 KMP next数组的运用

A Secret hdu 6153