快速介紹字符串比對KMP算法(Java代碼版)

一、問題的由來

我們會在面試或者日常“搬磚”過程中遇到這類問題:有一個文本串S(比如“ababbbaccdddmmd”),有一個模式串Q(比如“baccdd”),判斷模式串Q是否是S的字串,如果是返回Q在S中的起始位置,如果不是返回-1。我們腦海裏第一個思路就是循環遍歷,如果當前字符匹配成功就繼續匹配下一個,否則S中的標記向後移動一位,Q的標記回到最開始,就會有如下代碼:

public static int kmpMatch(char[] chars, char[] ch) {
        //S的長度
        int sLen = chars.length;
        //Q的長度        
        int qLen = ch.length;
        int i = 0, j = 0;
        while (i < sLen && j < qLen) {
            if (chars[i] == ch[j]) {
                i++;
                j++;
            } else {
                //這裏是把i回到這一輪的開始位置後再向後移動一位
                i = i - j + 1;     // i - ( j - 1 )
                j = 0;
            }
        }
        if (j == qLen) return i - j;
        else return -1;
    }

這個解法是正確的,但是我們是否在每次匹配失敗的時候都需要另Q的標誌位都回到0呢?顯然是不需要,這就引入了KMP算法。

二、KMP算法

KMP算法和常規的暴力算法的區別就在於當S的字符和Q的字符不匹配時候的標記符號的移動,暴力解法是另i = i - j + 1,j = 0。在KMP算法中的核心思想是找到字符串Q中的前綴和後綴中的最長公共長度,讓i不變,j移動最長公共字串的長度,這樣就可以有效的避免了重複字符的移動,所以只需要求出j的嚇一跳的數組就可以了我這裏說的比較簡單,我特意找了我當時學習時的一篇博客供大家學習 KMP詳解,回到本文,什麼是最長公共前後綴的長度呢? 

以下說明以“ABCDABD”爲例進行說明:

    -"A"的前綴和後綴都爲空集,共有元素的長度爲0;

  -"AB"的前綴爲[A],後綴爲[B],共有元素的長度爲0;

  -"ABC"的前綴爲[A, AB],後綴爲[BC, C],共有元素的長度0;

  -"ABCD"的前綴爲[A, AB, ABC],後綴爲[BCD, CD, D],共有元素的長度爲0;

  -"ABCDA"的前綴爲[A, AB, ABC, ABCD],後綴爲[BCDA, CDA, DA, A],共有元素爲"A",長度爲1;

  -"ABCDAB"的前綴爲[A, AB, ABC, ABCD, ABCDA],後綴爲[BCDAB, CDAB, DAB, AB, B],共有元素爲"AB",長度爲2;

  -"ABCDABD"的前綴爲[A, AB, ABC, ABCD, ABCDA, ABCDAB],後綴爲[BCDABD, CDABD, DABD, ABD, BD, D],共有元素長度爲0。

所以就得到了這樣一個數組next[7]{0,0,0,0,1,2,0},所以每次只需要另j移動next[k]位就可以了,k就是每次不匹配字符的位置。於是,代碼就可以寫成如下:

    private static int kmpMatch(char[] chars, char[] ch) {
        int sLen = chars.length;
        int pLen = ch.length;
        int[] next = getNextArray(ch);
        for (int i : next) {
            System.out.print(i+" ");
        }
        int i = 0, j = 0;
        while (i < sLen && j < pLen) {
            if (chars[i] == ch[j]) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == pLen) return i - j;
        else return -1;
    }

    public static int[] getNextArray(char[] sub) {
        int[] next = new int[sub.length];
        next[0] = -1;
        next[1] = 0;
        int k;
        for (int j = 2; j < sub.length; j++) {
            //System.out.println(j);
            k = next[j - 1];
            while (k != -1) {
                if (sub[j - 1] == sub[k]) {
                    next[j] = k + 1;
                    break;
                } else {
                    k = next[k];
                }
                next[j] = 0;
            }
        }
        return next;
    }

通過getNextArray函數就可以獲得模式串Q的下一跳位置的數組,這樣就得到了KMP算法的代碼。如果還有什麼不明白的可以留言,我會進行解答的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章