KMP中next數組的理解與應用


 理解

1、next數組一直往前走

next數組一直往前走,得到的所有前綴也是當前主串的后綴,當然了,也是當前主串的前綴。

2、周期性字符串

1、周期性字符串$\Leftrightarrow n \,\% \, (n-next[n]) == 0 \ \&\& \ next[n] {\ } {\!}!{=} \  0 $,循環節長度是$n-next[n]$。

2、next數組往前跳的步長是一樣的,除了最后一次。即$i-next[i]$保持恆定。

應用

思路:先求出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 }
View Code

 

思路:先求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     //int n = strlen(s);
14     while (i < n)
15     {
16         while (j != -1 && s[i] != s[j])  j = nexts[j];    //當前不匹配,j回退,尋找是否存在一個長度較小的字串和開頭的字串相等
17         nexts[++i] = ++j;                //j等於已匹配的長度,如果當前位置也匹配,則nexts直接為j+1
18     }
19 }
20 
21 void slove()
22 {
23     pre_kmp();
24     if (n % (n - nexts[n]) != 0)  printf("1\n");
25     else  printf("%d\n", n / (n - nexts[n]));
26 }
27 
28 int main()
29 {
30     int T = 0;
31     while (scanf("%s", s) == 1 && s[0] != '.')
32     {
33         n = strlen(s);
34         s[n] = '#'; 
35         slove();
36     }
37     return 0;
38 }
View Code

思路:求所有前綴出現的次數和,由於next數組回退得到的前綴也是主串的后綴,所以所有next回退的次數加上本身的一次取模就是答案。

代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 const int mod = 10007;
 7 const int maxn = 200000 + 10;
 8 int nexts[maxn], n;
 9 char s[maxn];
10 
11 void pre_kmp()
12 {
13     int i = 0, j = nexts[0] = -1;
14     //int n = strlen(s);
15     while (i < n)
16     {
17         while (j != -1 && s[i] != s[j])  j = nexts[j];    //當前不匹配,j回退,尋找是否存在一個長度較小的字串和開頭的字串相等
18         nexts[++i] = ++j;                //j等於已匹配的長度,如果當前位置也匹配,則nexts直接為j+1
19     }
20 }
21 
22 void slove()
23 {
24     int ans = n;
25     pre_kmp();
26     for (int i = n; i > 0; i--)
27     {
28         int k = nexts[i];
29         while (k > 0)
30         {
31             ans = (ans + 1) % mod;
32             k = nexts[k];
33         }
34     }
35     printf("%d\n", ans);
36 }
37 
38 int main()
39 {
40     int T;
41     scanf("%d", &T);
42     while (T--)
43     {
44         scanf("%d", &n);
45         scanf("%s", s);
46         slove();
47     }
48     return 0;
49 }
View Code

思路:題目大意:問至少添加多少個字符,使得這個字符串有至少兩個循環節。若本身有兩個循環節返回0,否則補充至兩個循環節。

代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 const int mod = 10007;
 7 const int maxn = 200000 + 10;
 8 int nexts[maxn], n;
 9 char s[maxn];
10 
11 void pre_kmp()
12 {
13     int i = 0, j = nexts[0] = -1;
14     n = strlen(s);
15     while (i < n)
16     {
17         while (j != -1 && s[i] != s[j])  j = nexts[j];    //當前不匹配,j回退,尋找是否存在一個長度較小的字串和開頭的字串相等
18         nexts[++i] = ++j;                //j等於已匹配的長度,如果當前位置也匹配,則nexts直接為j+1
19     }
20 }
21 
22 void slove()
23 {
24     pre_kmp();
25     int len = n - nexts[n];
26     if (n % len  == 0 && nexts[n] != 0)  printf("0\n");
27     else  printf("%d\n", len - nexts[n] % len);
28 }
29 
30 int main()
31 {
32     int T;
33     scanf("%d", &T);
34     while (T--)
35     {
36         scanf("%s", s);
37         slove();
38     }
39     return 0;
40 }
View Code

 

 

參考鏈接:

1、https://vjudge.net/contest/278481#overview

2、https://blog.csdn.net/hkh746392783/article/details/52015293


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM