简介:
KMP算法,适用于模式匹配,即查找模式串P在字符串S内的出现位置,其时间复杂度为O(M+N)。
(题外话:模式匹配问题是算法竞赛的常客,但理解KMP算法具有一定难度,建议先理解BF算法后再理解KMP算法。当然,网上关于KMP算法的讲解很多,这里仅仅只是给出模板。)
模板:
下面给出2种求next数组的方法,其中优化next数组求法虽然使得kmp算法更快,但针对需要输出模式串p的next数组的算法题,往往多数都是要原方法求出next数组。两种方法求出的next数组有什么区别可以通过网上关于KMP算法的讲解了解到。
1 //求出模式串p的next数组
2 void Next(char* p,int *next)
3 {
4 int pLen = strlen(p);
5 next[0] = -1;
6 int k = -1;
7 int j = 0;
8
9 while (j < pLen){
10 //p[k]表示前缀,p[j]表示后缀
11 if (k == -1 || p[k] == p[j]) {
12 ++k;
13 ++j;
14 next[j] = k;
15 }else{
16 k = next[k];
17 }
18 }
19 }
1 //优化next数组求法
2 void Next(char* p, int *next)
3 {
4 int pLen = strlen(p);
5 next[0] = -1;
6 int k = -1;
7 int j = 0;
8
9 while (j < pLen)
10 {
11 if (k == -1 || p[k] == p[j]){
12 ++j;
13 ++k;
14 //改动在下面4行
15 if (p[j] != p[k])
16 //之前只有这一行
17 next[j] = k;
18 else
19 //为了避免出现p[j] = p[ next[j] ],需要再次递归
20 next[j] = next[k];
21 }else{
22 k = next[k];
23 }
24 }
25 }
下面给出不同版本的kmp算法代码,核心算法不变,但根据目的不同进行了相应的修改。
1 //返回模式串p在字符串s中首次出现的位置
2 int KMP(char* s, char* p)
3 {
4 int i = 0;
5 int j = 0;
6 int sLen = strlen(s);
7 int pLen = strlen(p);
8
9 while (i < sLen && j < pLen){
10 //如果j = -1,或当前字符匹配成功(即S[i] == P[j]),都令i++,j++
11 if (j == -1 || s[i] == p[j]){
12 i++;
13 j++;
14 }
15 //如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
16 else{
17 j = next[j];
18 }
19 }
20
21 if (j == pLen)
22 //返回匹配到的位置
23 return i - j+1;
24 else
25 //匹配失败,返回0
26 return 0;
27 }
1 //输出模式串p在字符串s中出现的所有位置
2 void kmp(char *s,char *p)
3 {
4 int pLen=strlen(p);
5 int sLen=strlen(s);
6 int i=0;
7 int j=0;
8
9 while(i<sLen && j<pLen)
10 {
11 if(j==-1 || s[i]==p[j])
12 {
13 i++;
14 j++;
15 }else{
16 j=next[j];
17 }
18
19 if(j==pLen)
20 {
21 //输出匹配到的位置
22 cout<<i-j+1<<endl;
23 //j进行递归,i不需要改变
24 j=next[j];
25 }
26 }
27 }
例题:洛谷P3375
(一道模板题)
1 const int Size = 1000000+7;
2
3 char s[Size],p[Size];
4 int next[Size];
5
6 void Next(char *p,int *next)
7 {
8 int pLen=strlen(p);
9 int k=-1;
10 int j=0;
11 next[0]=-1;
12
13 while(j<pLen)
14 {
15 if(k==-1 || p[j]==p[k])
16 {
17 j++;
18 k++;
19 next[j]=k;
20 }else{
21 k=next[k];
22 }
23 }
24 }
25
26 void kmp(char *s,char *p)
27 {
28 int pLen=strlen(p);
29 int sLen=strlen(s);
30 int i=0;
31 int j=0;
32
33 while(i<sLen && j<pLen)
34 {
35 if(j==-1 || s[i]==p[j])
36 {
37 i++;
38 j++;
39 }else{
40 j=next[j];
41 }
42
43 if(j==pLen)
44 {
45 cout<<i-j+1<<endl;
46 j=next[j];
47 }
48 }
49 }
50
51 int main()
52 {
53 scanf("%s",s);
54 scanf("%s",p);
55
56 Next(p,next);
57 kmp(s,p);
58
59 //注:遍历从下标1开始
60 int pLen=strlen(p);
61 for(int i=1;i<=pLen;i++)
62 {
63 printf("%d ",next[i]);
64 }
65 }