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

你可能感兴趣的:(KMP)