1. 程式人生 > >字串匹配——KMP演算法的Java實現

字串匹配——KMP演算法的Java實現

開始複習演算法,複習到字串這一結構時,一個經典的問題就是兩個字串的匹配問題。
比如:在主串ssdfgasdbababa中找是否存在一個asdba的子串。

傳統方法——暴力匹配

用傳統的方法就是暴力匹配,從主串中一個個地和子串匹配。
最壞的情況下,就是匹配到最後一步才得到結果。
其時間複雜度為O((m-n)*n),其中主串的長度為m,子串的長度為n。

KMP演算法

以下為自己的理解,表達上難免有些口語化:)
KMP演算法的核心思想就是:如果A==B,B!=C,那麼A必然不等於C!,這樣一來就省掉了比較A和C的步驟。
KMP的大致思想就是:利用一個next陣列,儲存子串中每個字元前面的字首和字尾的最大相同匹配長度,為了在與主串匹配時,如果存在相同的部分,就可以通過next陣列來“跳過”已經相同的部分,省掉一些匹配操作。


如果想看詳細解釋,推薦另一篇部落格:KMP演算法最淺顯理解——一看就明白,不過提醒下這篇是基於C++實現的,不過原理講的挺好的。

程式碼實現與理解

在KMP演算法上分兩部分:一部分是計算next陣列,另一部分就是子串和主串匹配。其實兩者實現原理是一致的。

計算next陣列

首先計運算元串的next陣列
在這裡要解釋下,不同版本的KMP演算法雖然原理一樣,但是各自對next的值有不同的理解,在這裡說明一下,next陣列中:

  • 為0:代表字首和字尾沒有匹配的,同時預設子串第一個元素的next值為0,比如abc每個字元的next值都為0;
  • 為1:代表字首和字尾有一個字元匹配的,比如aba
    中,最後一個字元a的next值為1;
  • 為2:代表字首和字尾有兩個字元匹配的,比如abab中,最後一個字元b的next值為2;
  • 以此類推3,4,5······
    public static int[] calNext(String str){
        int length = str.length();      //子串的長度
        int j = 0;                      //j是next陣列的下標
        int[] next = new int[length];   //初始化子串
        next[0] = 0;                    //子串的第一個元素肯定沒有匹配的,預設為0
for(int q = 1; q<length; q++){ while(j>0 && str.charAt(j)!=str.charAt(q)){ //如果不相等 //j下標表示next陣列的index,q下標表示子串的index //j>0說明前面仍有部分匹配的情況 //注意:在next陣列中next[j]永遠小於或等於j的,保證當不相等的情況下,夠往前回溯 j = next[j-1]; //回溯 } if (str.charAt(q)==str.charAt(j)) { //如果相同,則j加1 j++; } next[q] = j; } return next; }

利用next陣列,匹配主串和子串

KMP主方法思想和上面求next陣列思想是一致的,只不過一個是串內匹配,這個是兩個串之間匹配。
直接上程式碼:

//str為主串,dest為子串,next為上面得到的next陣列
public static int kmp(String str, String dest, int[] next){

        for(int i=0,j=0;i<str.length();i++){
            while(j>0&&str.charAt(i)!=dest.charAt(j)){  //說明子串和主串當前下標的元素不匹配
                //j>0說明前面有相同的部分
                j = next[j-1];    //回溯
            }
            if(str.charAt(i)==dest.charAt(j)){
                j++;
            }
            if(j == dest.length()){    //說明匹配到了子串末端,找到匹配的地方
                return i-j+1;
            }
        }
        return -1;    //如果找不到匹配,返回-1
    }

主程式

最後就是在主程式中呼叫了。

public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str = "ssdfgasdbababa";
        String dest = "ababa";
        int[] next =  calNext(dest);
        System.out.println(kmp(str, dest, next));
    }

總結

當時理解KMP演算法還是有點繞,稍微有些難度(原諒我演算法基礎不咋樣~)
最後說一下,KMP演算法在字串存在部分匹配才會體現它的效率,否則和一般方法區別不大。