我们考虑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()) != ' ') 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 ", ++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 ", i + 1, (i + 1) / delta); 29 } 30 } 31 putchar(' '); 32 } 33 }