KMP

被刻印的时光 ゝ 提交于 2019-12-05 19:39:21

简介:

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 }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!