串的模式匹配算法——“KMP算法”

写在文章前:我写这篇文章,一来为了整理和记录自己对KMP算法的一些理解,二来分享给大家学习和交流。2016-03-13

  1.何谓KMP算法?——粗略介绍

     这种算法是D.E.Knuth、J.H.Morris 和 V.R.Pratt 同时发现的,所以各取他们名字的首字母命名这个算法——KMP。它是解决串的模式匹配问题的一种算法,举个例子说明一下KMP算法是解决什么问题的:比如字符串(主串) abbcabcabcbc , 子串(模式串) bcabcb ,在主串中找出字串的第一个字符出现的位置(匹配结果明显是6)。

   这个问题的解法,最简单的就是可以采用枚举法,所谓的BF算法。

            

        对比于BF算法,KMP算法在匹配策略上做出了一些改进。KMP算法在每一趟匹配过程中出现不等时,可以利用已经得到的“部分匹配”的结果将模式向右“滑动”到下一个最佳尝试位置,然后继续比较.

      

  2.KMP原理详解

    KMP利用了已有的匹配结果,在主串与模式串不匹配时一下子滑动到已匹配的模式串前段中某个位置,直接可以继续匹配该位置后的串,而不需担心前面的串。我们可以观察下串的匹配过程。

    

    其实,框3和框4的匹配情况只跟模式串有关,所以我们可以在模式匹配前先通过模式串得到模式串中各元素下次匹配的最佳位置,因为每个元素都可能出现不匹配情况,所以需要保存有所有元素的下次匹配位置,即求出kmp算法中的next数组。什么是next数组?我们定义next数组为,next[i]表示的是当主串的k位置与模式串的i位置不匹配时(主串长〉k〉=i),主串k位置下一步应该与模式串的next[i]位置进行匹配。怎么求next数组?next[j]= 模式串前段p1 ··· pj-1中头尾相同的最长头串或尾串的长+1。(注:头串简称以串头元素开头的串,尾串简称以串尾元素结尾的串。)                                             

  1.            next[j]=  0      当j =1时
  2.        next[j]= max{k|0<k<j , p1 ··· pk-1 =pj-k+1···pj-1}   //这个式子等同于模式串P1~Pj-1的头串与自身(p1~pj-1)的尾串进行模式匹配,只不过这里是得到被匹配的串(P1-Pk-1)的串长+1,即k。
  3.            next[j]=  1      除了1、2情况之外的其他情况 

比如:

 j 1 2 3 4 5 6 7 8 9
 p[j] a b a a b c a b c
  0                
    1              
      1            
  -   - 2          
      - 2        
  - -   - - 3      
              1    
            - 2  
   - -         - - 3
 next[j] 0 2 2 3
 j 1 2 3 4 5 6 7 8 9
 p[j] a a a a a a a a a
  0                
  - 1              
  - - 2            
  - = - 3          
  - = = - 4        
  - = = = - 5      
  - = = = = - 6    
  - = = = = = - 7  
  - = = = = = = - 8
 next[j]  0

 

               

  3.KMP算法编码

void getNext(char* pattern,int* next){
    //填充模式串的next数组,原理也是kmp算法的原理。
    int i=1,j=0;                        //用i作为主串下标,j作为模式串的下标。
    next[1]=0;
    while(i<pattern[0]){                //保证i是合法值,数组0号单元存放数组长度
        if(j==0||pattern[i]==pattern[j]){//j已经回溯出了串头或者匹配成功,表示
    //没意义再对当下的i、j进行匹配,需要右移i和j,进行下一个匹配
         ++i;
            ++j;
            next[i]=j;
        }
        else
            j=next[j];                   //匹配不成功,模式串需要回溯到next[j]的位置。
    }
}

int KMP(char* s,char* pattern,int* next){
//主串s,模式串pattern,已填充完的next数组,返回模式匹配的结果
    int i=1,j=1;
    while(i<=s[0]&&j<=pattern[0]){
        if(j==0||s[i]==pattern[j]){
            ++i;
            ++j;
        }
        else
            j=next[j];
    }
    if(i>pattern[0]) return i-pattern[0];
    else return 0;
}

 

posted @ 2016-03-13 19:25  蓝盈盈  阅读(676)  评论(0)    收藏  举报