[转载]KMP和拓展KMP

原文转自: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 为已经匹配字符串的长度)
  两个与kmp相关的简单题目:POJ 2406  ,POJ 1961
  求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

posted @ 2013-05-08 23:42  冰鸮  阅读(180)  评论(0编辑  收藏  举报