理解
1、next数组一直往前走
next数组一直往前走,得到的所有前缀也是当前主串的后缀,当然了,也是当前主串的前缀。
2、周期性字符串
1、周期性字符串$\Leftrightarrow n \,\% \, (n-next[n]) == 0 \ \&\& \ next[n] {\ } {\!}!{=} \ 0 $,循环节长度是$n-next[n]$。
2、next数组往前跳的步长是一样的,除了最后一次。即$i-next[i]$保持恒定。
应用
- 题目一:Period
思路:先求出next数组,然后遍历一遍next数组得到所有字符结尾的字符串循环节的长度及个数
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int maxn = 1000000 + 10; 7 int nexts[maxn],n; 8 char s[maxn]; 9 10 void pre_kmp() 11 { 12 int i = 0, j = nexts[0] = -1; 13 while (i < n) 14 { 15 while (j != -1 && s[i] != s[j]) j = nexts[j]; //当前不匹配,j回退,寻找是否存在一个长度较小的字串和开头的字串相等 16 nexts[++i] = ++j; //j等于已匹配的长度,如果当前位置也匹配,则nexts直接为j+1 17 } 18 } 19 20 void slove() 21 { 22 pre_kmp(); 23 for(int i = 2; i <= n; i++) 24 if (i % (i - nexts[i]) == 0 && nexts[i] != 0) printf("%d %d\n", i, i / (i - nexts[i])); 25 } 26 27 int main() 28 { 29 int T = 0; 30 while (scanf("%d",&n) == 1 && n) 31 { 32 scanf("%s", s); 33 s[n] = '#'; 34 if (T) printf("\n"); 35 printf("Test case #%d\n", ++T); 36 slove(); 37 } 38 return 0; 39 }