poj1961 Period

我们考虑KMP算法中fail失配指针的意义。

对于一个模式串(Pattern),位置i对应的失配指针fail[i]是那个位置:

这个位置满足的条件是,子串[0, fail[i])是位置i(不含)的后缀,并且fail[i]是所有满足此条件的位置中最靠后(最接近i)的那个。

也就说当我们用模式串P匹配文本串T的时候,我们检查当前位置T[i]与P[j]是否匹配,

若匹配i,j指针各向前移动一位;

否则,利用模式子串[0, j)与文本子串i(不含)的后缀已经匹配的信息,保持i指针不变,j指针退到fail[j]位置再次尝试匹配。

退回的指针位置同样应该满足P的前缀与i(不含)后缀相匹配。

那么一个模式串fail指针蕴含着当前位置后缀与模式串前缀匹配的信息。

回到本题,考虑位置i(>0),若i位置是某个重复节(长度设为len)的终点,那么其失配指针必然指向(i - len)位置,

并且满足len | (i + 1) && str[i - len] == str[i]。

反之我们有满足此条件的必然是某个重复节的终点:

因为[0, fail[i]]可用字符串拼接表示:s1 + s2 + ... + sk + t,其中strlen(si) = len,

那么[len, i]必然是s2 +... + sk + t + t。

有s1 = s2 && s2 == s3 &&... && sk -1 = sk && sk = t。

可见该条件对于所求是等价的。

 

http://poj.org/problem?id=1961

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn = 1e6 + 10;
 6 char str[maxn];
 7 int n;
 8 int fail[maxn];
 9 
10 int main(){
11     //freopen("in.txt", "r", stdin);
12     int kase = 0;
13     while(~scanf("%d", &n) && n){
14         char ch, *p = str;
15         while((ch = getchar()) != '\n') ch = getchar();
16         for(int i = 0; i < n; i++) *(p++) = getchar();
17         fail[0] = fail[1] = 0;
18         for(int i = 1; i < n; i++){
19             //compute forward not backward
20             int j = fail[i];
21             while(j && str[j] != str[i]) j = fail[j];
22             fail[i + 1] = str[j] == str[i] ? j + 1 : 0;
23         }
24         printf("Test case #%d\n", ++kase);
25         for(int i = 1; i < n; i++){
26             int delta = i - fail[i];
27             if((i + 1) % delta == 0 && str[i] == str[fail[i]]){
28                 printf("%d %d\n", i + 1, (i + 1) / delta);
29             }
30         }
31         putchar('\n');
32     }
33 }
View Code

 

posted @ 2015-10-10 16:53  astoninfer  阅读(152)  评论(0编辑  收藏  举报