字串匹配之KMP《演算法很美》

羅彬樺發表於2021-01-04

字串匹配之KMP

思路: 其實就是暴力匹配的改版嘛!要想理解這道題就必須一步一步的去試。

先理解next
next其實就是找前字尾數量
比如:

  • 模串 :b, a, b, b, a, b, b
  • 下標 :0, 1, 2, 3, 4, 5, 6
  • next :0, 0, 1, 2, 3, …

_ _ _ _ _ b "b"的字首和字尾都為空集,共有元素的長度為0。
_ _ _ _ b a "ba"的字首為"b",字尾為"a",共有元素的長度為0。
_ _ _ b a b "bab"的字首為"ba",字尾為"ab",共有元素的長度為1。都有個a。
_ _ b a b a "baba"的字首為"bab",字尾為"aba",共有元素的長度為2。
_ b a b a b "babab"的字首為"baba",字尾為"abab",共有元素的長度為3。
b a b a b b 省略。。

發現一個問題沒有,如何

具體思路:

  1. int pLength = ps.length(); 獲取ps的長度。
  2. int[] next = new int[pLength];開闢根據ps長度的next空間
  3. char[] p = ps.toCharArray(); 將字串p轉成字元陣列
  4. next[0] = -1;將第一個next設定好
  5. if (ps.length() == 1) 判斷是否長為1,如果只有1就直接返回 return next;
  6. next[1] = 0; 設定第二個next的值
  7. next[] = {-1, 0 , } p[] = {b, a, b, b, a, b, b} 設一個長的
  8. int j = 1; int k = next[j]; k = 0;
  9. 判斷 if (k < 0 || p[j] == p[k]) 此時k!<0,p[1] (a) == p[0] (b) 不相等
  10. k = next[0]; 此時 k = -1;
  11. 再判斷 此時 k<0 , next[++j] = ++k; next[] = {-1, 0 , 0}
public class KMP {
    public static void main(String[] args){
        String src = "babababcbabababb";
        int index = indexOf(src, "bababb");
        index = indexOf(src, "bab");
        System.out.println(index);
    }

    private static int indexOf(String s, String p) {
        if (s.length() == 0 || p.length() == 0) return -1;
        if (p.length() > s.length()) return -1;

        int count = 0;
        int[] next = next(p);
        int i = 0;//s位置
        int j = 0;//p位置
        int sLen = s.length();
        int pLen = p.length();

        while (i < sLen) {
            //①如果 j = -1,或者當前字元匹配成功(即S[i] == P[j]),都令i++,j++
            //j=-1,因為next[0]=-1,說明p的第一位和i這個位置無法匹配,這時i,j都增加1,i移位,j從0開始
            if (j  == -1 || s.charAt(i) == p.charAt(j)) {
                i++;
                j++;
            } else {
                //②如果j != -1,且當前字元匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]
                //next[j]即為j所對應的next值
                j = next[j];
            }
            if (j == pLen) { //匹配成功了
                count++;
                i--;
                j=next[j-1];
//                return (i - j);
            }
        }
        return count;
    }

    private static int[] next(String ps) {
        int pLength = ps.length();
        int[] next = new int[pLength];
        char[] p = ps.toCharArray();
        next[0] = -1;
        if (ps.length() == 1)
            return next;
        next[1] = 0;

        int j = 1;
        int k = next[j]; //看看位置j的最長匹配的字首在哪裡

        while (j < pLength - 1) {
            //現在要推出next[j+1],檢查j和k位置上的關係即可
            if (k < 0 || p[j] == p[k]) {
                next[++j] = ++k;
            } else {
                k = next[k];
            }
        }
        return next;
    }
}

相關文章