简介:
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 }