kmp算法的自我理解


   

 

序:很久沒做算法題了,為了回顧一下自己的算法知識,方便下次理解,特地記錄自己一些對一些算法的理解。

 

約定:

模式串  ababcd

文本串  abababcd

M代表模式串,W代表文本串

 

 

kmp算法包括兩個部分,1.計算模式串的next數組。 2.kmp主程序,模式串與主串(即文本串)的匹配。

 

Next數組

next[i]表示字符串第i個字符可匹配的最近的下標(挺拗口的),作用是記錄已經遍歷過的字符內的信息。

先看計算next數組的程序,

 

    private static int[] getNext(char[] str) {
        int length = str.length;
        int[] next = new int[length];
        next[0] = -1;
        
        int k = -1;
        for(int i=1; i<length; i++) {
            while(k > -1 && str[i] != str[k+1]){
                k = next[k];
            }
            if(str[i] == str[k+1]) {
                k++;
            }
            next[i] = k;
        }
        return next;
    }

字母下方即是模式串的next數組

a  b  a  b   c  d

-1 -1  0  1  -1  -1

從結果來看,我們可以看到next[3] = 1,含義是它的可匹配的最近的下標是1。其實從這時候來看還是覺得沒什么作用,我們下面在匹配文本的時候具體分析。

 

kmp主程序

下面是kmp主算法:

   1:      public static int kmp(final char[] text, final char[] pattern) {
   2:          // 檢查是否為空
   3:          if(isEmpty(text) || isEmpty(pattern)) {
   4:              return -1;
   5:          }
   6:          
   7:          int lengthText = text.length;
   8:          int lengthPattern = pattern.length;
   9:          int[] next = getNext(pattern); // 獲取模式串的next數組
  10:          
  11:          int k = -1;
  12:          for(int i=0; i<lengthText; i++) {
  13:              while(k > -1 && text[i] != pattern[k+1]) {
  14:                  k = next[k];           // 若不相等則找出與k可匹配的最近的下標
  15:              }
  16:              if(text[i] == pattern[k+1]) {
  17:                  k++;
  18:              }
  19:              if(k == lengthPattern - 1) {
  20:                  // 表示找到了,再賦值k,繼續下次尋找
  21:                  System.out.println("find position is " + (i - lengthPattern + 1));
  22:                  k = next[k];
  23:              }
  24:          }
  25:           
  26:          return -1;
  27:      }

看了上面的代碼,可以發現其實和計算next數組的代碼差不多。其實他們的本質是一樣的,計算next數組是模式串匹配自己,而kmp主程序是模式串匹配文本串。

 

匹配過程

從文本串的第一個字母開始匹配,文本串下標為int  i,初始化為0。模式串的下標為k+1,k為int型,k初始化為-1。

下面是遍歷到i = 4, k = 3,時的情況,當發現兩者不相同時,k=next[k],所代表的意義是模式串的下標k位置的字符與前面的字符串哪一個字符最近可匹配,就是這個步驟可以省去和前面字符比較的時間(因為前面都相同)。所以當你發現文本串i位置和模式串j位置匹配不成功時,你可以比較模式串(next[j-1] +1)和文本串的i位置是否相等,如不相等如此循環查找(kmp主程序的13~15行)。所以此時的情況是k = next[k]之后k = 1,然后比較M(k+1)與W(i),我們發現此時是相等的,這一步就是kmp和普通模式串匹配的差別,它省去了再去從模式串的開頭進行比較,而是直接從下標2開始比較。

0 1  2 3  4 5  6  7

a  b  a  b a b  c  d

a  b  a  b c  d

 

kmp的算法復雜度(O(n))

設主串的長度為n,模式串為m,n>m。

我們可以這樣計算,首先是對主串的遍歷,復雜度是n,主串的循環內的while循環,我們可以追蹤k的變化,k++的過程只能在每次循環時進行,k=next[k]是k的減少,k++最多發生n次,k=next[k]發生最多的次數是n/m *m = n,所以總的算法復雜度是3*n,就是O(n)的。

 

kmp的用處場景

1.查找模式串是否在目標串

2.計算字符串是由哪個或哪幾個字符串循環而來

3.查找模式串在目標串的哪些位置

4.最長公共子串

 

kmp的優化

以上我對kmp的理解來自於《算法導論》這本書,其實kmp還可以優化。一位大牛在網上論述了他對kmp的優化,下面是他的鏈接http://www.if-yu.info/2010/11/23/kmp-complexity.html

他的kmp思想跟導論不同在於next數組--直接記錄每個字符匹配失敗后應該去匹配哪個字符,這樣就可以優化重復匹配失敗的字符,而導論檢查的是前一個字符。

 

后記:轉載請標明出處,謝謝。  ----- doubleHHHH


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM