原文转自:http://jijiwaiwai163.blog.163.com/blog/static/1862962112012623105531177/
1、KMP算法
KMP算法是一种改进后的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。通过一个辅助函数实现跳过扫描不必要的目标串字符,以达到优化效果。
KMP(O(n+m))算法与传统的BF算法(O(n*m))想比自然快了许多。
KMP(Knuth-Morris-Pratt)算法核心思想是:在发生失配时,主串不需要回溯,而是利用已经得到的“部分匹配”结果将模式串右移尽可能远的距离,继续进行比较。这里要强调的是,模式串不一定向右移动一个字符的位置,右移也不一定必须从模式串起点处重新试匹配,即模式串一次可以右移多个字符的位置,右移后可以从模式串起点后的某处开始试匹配。
对于next[]数组有位移d=len-next[len]可以看作是构成字符串s的字串(如果n%d==0,存在这样的构成),相应的重复次数也就是n/d。(其中len 为已经匹配字符串的长度)
求next[]算法模板:(未优化)
1 void getNext(char s[],int next[]) 2 { 3 int length=strlen(s); 4 int i=0,j=-1; 5 next[0]=-1; 6 while(i<length) 7 { 8 if(j==-1||s[i]==s[j]) 9 { 10 ++i; 11 ++j; 12 next[i]=j; 13 } 14 else 15 j=next[j]; 16 } 17 }
求next[]算法模板:(已优化)
1 void getNextval(char s[],int nextval[]) 2 { 3 int length=strlen(s); 4 int i=0,j=-1; 5 nextval[0]=-1; 6 while(i<length) 7 { 8 if(j==-1||s[i]==s[j]) 9 { 10 ++i; 11 ++j; 12 //next[i]=j; 13 if (s[i]!=s[j]) 14 nextval[i]=j; 15 else 16 nextval[i]=nextval[j]; 17 } 18 else 19 j=nextval[j]; 20 } 21 }
KMP匹配
1 int KMP( char *t, char *s ) //s为主串,t为模式串 2 { 3 int lenth = strlen(t); 4 int len = strlen(s); 5 GetNextVal( t, lenth ); 6 int i = 0, j = 0; 7 while ( j < len ) 8 { 9 if ( i == -1 || s[j] == t[i] ) 10 { 11 ++i, ++j; 12 if ( i == lenth ) return j; 13 } 14 else i = nextval[i]; 15 } 16 return -1; 17 }
2、扩展KMP算法
扩展kmp既是求模式串和主串的每一个后缀的最长公共前缀
即令s[i]表示主串中以第i个位置为起始的后缀,则B[i]表示s[i]和模式串的最长公共前缀
显然KMP是求s[i]=模式串长度的情况,所以,扩展KMP是对KMP的拓展
像求KMP的next数组一样,我们先求A[i],表示模式串的后缀和模式串的最长公共前缀
然后再利用A[i]求出B[i]
说明一下A的求法,B同理
现在我们要求A[i],且A[1]---A[i-1]已经求出,设k,且1<=k<=i-1,并满足k+A[k]最大
所以T[k]--T[k+A[k]-1]=T[0]--T[A[k]-1],推出T[i]--T[k+A[k]-1]=T[i-k]--T[A[k]-1]
令L=A[i-k],若L+i-1<k+A[k]-1,由A是最长公共前缀知A[i]=L,否则,向后匹配,知道字符串失配
并相应更新k
时间复杂度为线性O(m+n)
模板:
1 int next[maxn],extend[maxn]; //extend[i]表示原 串以第i开始与模式串的前缀的最长匹配 2 void EKMP(char s[],char t[])//s[]为主串,t[]为模版串 3 { 4 int i,j,p,l; 5 int len=strlen(t); 6 int len1=strlen(s); 7 memset(next,0,sizeof(next)); 8 memset(extend,0,sizeof(extend)); 9 next[0]=len; 10 j=0; 11 while(1+j<len&&t[j]==t[1+j])j++; 12 next[1]=j; 13 int a=1; 14 for(i=2; i<len; i++) 15 { 16 p=next[a]+a-1; 17 l=next[i-a]; 18 if(i+l<p+1)next[i]=l; 19 else 20 { 21 j=max(0,p-i+1); 22 while(i+j<len&&t[i+j]==t[0+j])j++; 23 next[i]=j; 24 a=i; 25 } 26 } 27 j=0; 28 while(j<len1&&j<len&&s[j]==t[j])j++; 29 extend[0]=j; 30 a=0; 31 for(i=1; i<len1; i++) 32 { 33 p=extend[a]+a-1; 34 l=next[i-a]; 35 if(l+i<p+1)extend[i]=next[i-a]; 36 else 37 { 38 j=max(0,p-i+1); 39 while(i+j<len1&&j<len&&s[i+j]==t[j])j++; 40 extend[i]=j; 41 a=i; 42 } 43 } 44 }
str编号是从0~len-1,而next值编号是从1~len