Problem 1901 Period II
Accept: 676 Submit: 1825
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
For each prefix with length P of a given string S,if
S[i]=S[i+P] for i in [0..SIZE(S)-p-1],
then the prefix is a “period” of S. We want to all the periodic prefixs.
Input
Input contains multiple cases.
The first line contains an integer T representing the number of cases. Then following T cases.
Each test case contains a string S (1 <= SIZE(S) <= 1000000),represents the title.S consists of lowercase ,uppercase letter.
Output
For each test case, first output one line containing "Case #x: y", where x is the case number (starting from 1) and y is the number of periodic prefixs.Then output the lengths of the periodic prefixs in ascending order.
Sample Input
4oooacmacmacmacmacmafzufzufzufstostootssto
Sample Output
Case #1: 31 2 3Case #2: 63 6 9 12 15 16Case #3: 43 6 9 10Case #4: 29 12
Source
FOJ有奖月赛-2010年05月
跑一边len - Next[j] ,j=Next[j]
原理是什么呢 性质2:kmp的next不断向前递归的过程可以保证对于每一个当前前缀,都有一段后缀与之对应
那么你进行递归的时候其实已经是头尾一样 恒匹配 也就可以拿去对应匹配
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
using namespace std;
const int MAX_N = 1000024;
int Next[MAX_N];
char mo[MAX_N];
int ans[MAX_N];
int n2;
void getnext(){
int i = 0,j = -1;
while(i<n2){
if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i] = j;}
else j = Next[j];
}
return ;
}
int main(){
int t;
scanf("%d",&t);
for(int k = 1;k<=t;k++){
memset(ans,0,sizeof(ans));
//memset(Next,0,sizeof(Next));
int cnt = 0;
scanf("%s",mo);
n2 = strlen(mo);
Next[0] = -1;
getnext();
int j = n2;
//ans[cnt++]=j-Next[j];
while(Next[j]!=0){
ans[cnt++] =n2-Next[j];
j = Next[j];
}
ans[cnt++] = n2;
printf("Case #%d: %d\n",k,cnt);
for(int i = 0;i<cnt;i++){
i==cnt-1?printf("%d\n",ans[i]):printf("%d ",ans[i]);
}
}
return 0;
}
这个人写的挺好的:
对于所有的方案,可以归结于以下几类(实际上就是一类,我们画图了而已)
首先,next[len]表示,从[0, next[len]])的区域和串最后面[len-next[len], next[len] )区域的字符串是一样的。(len表示当前串长度)
对于一个长度为13的串,next[13],显然就表示前面几个字母,和后面几个字母相同。 在图上就是s[0]...s[3] 和s[9]..s[12]相同。
显然P=9成立,因为s[0]=s[0+9] s[1]=s[1+9] s[2]=s[2+9] s[3]=s[3+9] 然后s[4]配对的时候,不存在s[4+9=13]13这个元素。
所以我们可以先通过这个状态,得到第一个合法解:P=9 (len - next[len])
经过next[4]=2,可以知道s[0]s[1] 和s[2]s[3]相同。
又因为s[0]..s[4]和s[9]..s[12]相同,所以s[0],s[1]和s[11],s[12] 相同。
从而得到P=11的解。 11 = len - next[4]
再然后显然,就是求next[2]了,因为这表示s[0] s[1]区间前缀和后缀相同的情况,后缀又等于整个串的后缀。
假设s[0]==s[1]的话,next[2]就是1
得到P=12 12=len-next[2]
然后next[1]=0,实际上已经算法结束了。这个时候的前缀等于后缀的部分已经是空集了。
对于next[len]是下图情况:
有交集部分。 显然s[0]....s[8] 和s[5]...s[13]是相同。
s[0]=s[5], s[1]=s[5]....s[i]=s[i+5]
得到一个答案P=5 LEN - next[LEN] = 14-9=5
下面情况略多。先看图上面的情况。(next[9]=6)
s[0]=s[3] -> s[0]=s[5]=s[8]=s[8] (因为next[14]=9,看蓝色的圈,他们是等价的,两个蓝色的圈是相同的对不对,然后左边蓝色圈里的首位相同,肯定右边蓝色圈里首位也相同。两个蓝色圈又相同,所以可以得到这样的式子)
s[0] = s[8]
s[1] = s[9]
...
得到的P=8. LEN - next[9] = 14-6=8。
对于图下面的情况也是类似,next[9]=4.
同时s[0]显然可以等于s[10],(第一个红圈和第三个红圈彼此等价)从而得出解, p = 14-4=10
然后递归下去,每一个next所指的字符串区域相同的前缀(从s[0]开始)和后缀,都可以对应上原串的前缀(从s[0]开始)和后缀(s[len-1]结尾)。
回到第一张图
对于所有的P,大致也就是这些情况了。我们需要证明,刚才上述的算法,能包含所有的情况。
对于上图所示的情况,
实际上是这样的。两个橘黄色是相同的。然后就回到了上面的状态了。所有的子状态都是可达的。所以这题就做完了。
先next[13]得到9
然后就是next[9]=4。
看起来只得到的P=9的情况,没有P=4的情况。
实际上漏了第一步……就是13-next[13]的情况……