1. 程式人生 > >通俗易懂的KMP演算法詳解(嚴蔚敏版C語言)

通俗易懂的KMP演算法詳解(嚴蔚敏版C語言)

    最近,需要複習KMP演算法的next陣列,然後回頭看半年多後的我回頭看半年多前自己綜合別人內容寫的介紹。     沒錯,自己也看不懂。然後,自己再根據自己的理解寫了一下理解透徹的筆記,方便理解記憶,當然,以前的程式碼解釋部分可以參考,筆記演算法思維和演算法的實現有一定的出入。望君諒解。(2018.9.30)

      

                                                                           這是一條時間分割線

_____________________________________________________________________________________________________

    從剛接觸演算法,感覺是跟不上的一片片片段,再往後自己看書理解,片段更是碎成一點點亂碼。在下愚鈍,在查詢多種部落格,結合書上(嚴蔚敏的資料結構)解釋和程式碼,詢問教師,後終於開竅。在此處引用了阮先生的部分思想,來通俗的講解一下KMP演算法。

   BMP演算法與BF演算法的根本區別

     解讀BMP之前,我們先來理解一下BMP演算法存在的理由。對於模式匹配,目前所學的最簡單的是BF演算法,即偏向於“暴力”匹配的方法。另外一種就是較為複雜BMP演算法了。而倆者的區別在於:BF演算法是時間複雜度相對高的,KMP則可以理解為用空間換時間。

     BF演算法:        逐個匹配主串字元,然後模式串j值回溯到1重新匹配。

     KMP演算法:       KMP只需要將j值模式串中j的位置回溯到next[j]位,而免除了前面不需要的匹配,以此來換取時間。

圖片:

 相比一個一個比較,同學們肯定會更加想詢問為什麼不直接從模式串第一個字元和主串相同字元的位置比較即:       

圖片:

     而BMP則在此基礎上更加的簡便了。

      接下來,我們來用有邏輯的語言來了解KMP演算法。而後,再解釋如何用程式碼實現該演算法。

     首先,我們從程式碼的實現效果來看,主串與模式串每次歷往KMP演算法,模式串中移動至K位與失配的(主串)的i位對其,而不需要像BF演算法一樣,一次次回溯從頭開始。

     那麼,問題來了。K為何物?

     在此之前,我們來介紹一下最長字首和最長字尾,和部分匹配。

     移動位數 =  已匹配值   -    部分匹配值     (其實就是直接從最長字首直接跳到與他相等的最長字尾那裡開始往後匹配)

上面都可以不用看

    效果大概是這樣子:

以模式串ABCDABD,文字串BBCABCDABABCDABCDABDE為例子展示最大公共元素長度(最長字首和他相等的最長字尾)。

     再來一遍:移動位數 =  已匹配值   -    最大公共元素長度

關鍵是計算next陣列的值。

計算next陣列的值,虛擬碼如下:

void get_next(SString T, int[] next){
     /*求模式串T的next函式值並存入陣列next*/
       i=1; next[1]=0; j=0;
       while(i<T.length)
        {
          if(j==0||T.ch[i]==T.ch[j])
            {
                ++i;++j;             //相符合,字首位數和字尾位數後退
                next[i]=j;
            }else{
                 j=next[j];    //前n位字首不符合後n位字尾,使i待定,往前一位退,探測n-1位字首和字尾是否符合
                 /**如果一直不符合,那麼將一直退到0,進入if語句,i++下一位探測**/
            }
        
       }
}

解釋next陣列程式碼運算過程

       next陣列next[1]、next[2]固定為0,1,除next後還有next修正值,這裡不講。

       以abaabc為例子(前面倆步自己按程式碼思維過一遍): 第一個j=1(a),i=3(a) 

     ps:總之就是,先從第一個跟第二個比較,若成功就右滑比較,假設比較到三位連續相等:最大公共元素長度為3,第四位不等。  因為新增一個比較位,字尾的開頭第一個改變需要重新比較(abc——>字尾:cba,;    abcd——>字尾:dcba),j回退一位,比較最大公共元素長度為3是否相等,不等繼續後退減一(i一直為改變)。直到j=0,進入if語句,i++,下一個比較。計算next值的過程實質是計算模式串各個子串的最大公共元素長度。詳細見最大公共元素長度效果圖。然後方便模式串匹配文字串的時候,模式串直接從最長字首直接跳到與他相等的最長字尾那裡開始往後匹配。

下面是kmp的虛擬碼:

int index_KMP(SString S ,SString T, int[] next){//S為文字串,T為模式串
   while(i<=S.length && j<=T.length){
    
    if(j==0 || S.char[i]==T.char[j]){
            j++;i++;            //後一位是否連續性匹配
   }else j=next[j];            //從模式串的第j位開始和後面匹配,就是直接從最長字首直接跳到與他相等的最長字尾那裡
   
   if(j>T.length)
   {
       return i-T.length;   //匹配成功
   }else{
    return 0;             //匹配失敗
   }


}