题目大意:给定一个字串,要你找到如果要使之成为循环串,在末尾需要的最小的字数(只能添加字符,不能删减字符)
首先联动一下之前做过的动态规划问题POJ 3280,当然了3280这一题是用的LD,因为他可以添加或者删除(加上修改也行,但是要改状态方程了)
而我们现在要讨论的这一题(HDU 3746)是有限制的,首先它只能添加不能删除,其次一定是要在末尾添加。
其实这样的题用kmp可以很快地解决,其实这样的题目是循环节的题目,之前我已经写过很多了,这一题是对循环节的深刻理解。
首先我们我们明白,对于kmp算法的next数组,如果一个字串是循环字串,那么他一定有len%(len-next[len])==0且next[len]>0,这个已经从1961等题论证了
现在我们就是要搞清楚,如果len%(len-next[len])!=0会如何,我们先来看一个例子
比如上个例子,他的最大前缀和后缀是abcdeabcd,如果要在末尾加上字符使他循环,那么一定是要加上e,使新的循环节是abcde,而abcde的长度刚好是len-next[len]
再如这个例子,最大前缀和后缀是abcabcab,末尾加上c循环节就是abc,刚好也是len-next[len]
对于这个,不存在最大前后缀,必须加上len才能使之成为循环字串,而next[len]刚好是0,也符合len-next[len]
那么我们是不是能下结论,无论对于什么字串,无论是否循环,最小的循环节一定是len-next[len]呢?答案是最小循环节一定满足这个结论
我们再来看第二组
我们把最大前后缀对齐,发现突出的部分的长度就是新的数组的循环节的长度,事实上,我们将任何数组按照这个方式对齐都有这个结论,就是突出的长度刚好是新的数组的循环节的长度,而这个长度刚好就是len-next[len](非严格证明),那么原数组按照新的循环节划分会剩下len%(len-next[len])个单元(注意len-next[len]要特判,因为可能会存在len-next[len]==0),接着我们就可以直接用len-len%(len-next[len])得到需要补齐的长度了,这就是循环节题目的一般做法。
参考:http://blog.csdn.net/u013480600/article/details/22954037
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <string.h> 5 6 using namespace std; 7 8 static char input[100010]; 9 static int _Next[100010]; 10 11 void Get_Next(const int); 12 13 int main(void) 14 { 15 int case_sum, len; 16 scanf("%d", &case_sum); 17 18 while (case_sum--) 19 { 20 scanf("%s", input); 21 len = strlen(input); 22 Get_Next(len); 23 24 if (_Next[len] == 0)//不存在循环节 25 printf("%d\n", len); 26 else if (len % (len - _Next[len]) == 0)//已经是循环节了 27 printf("0\n"); 28 else 29 printf("%d\n", len - _Next[len] - len % (len - _Next[len])); 30 } 31 return EXIT_SUCCESS; 32 } 33 34 void Get_Next(const int len) 35 { 36 int i = 0, k = -1; 37 _Next[0] = -1; 38 39 while (i < len) 40 { 41 if (k == -1 || input[i] == input[k]) 42 { 43 i++; 44 k++; 45 _Next[i] = k; 46 } 47 else k = _Next[k]; 48 } 49 }