1. 程式人生 > >字串匹配演算法之KMP演算法詳情

字串匹配演算法之KMP演算法詳情

package demo;

/*
字串匹配演算法
 */
public class StringKMP {

    //找出從第一個字元開始 子串T在主串S的第一個位置 如果沒有則返回-1
    public static int index(String S, String T) {
        int tag = 0;
        int i = 0;
        int j = 0;
        char[] s = S.toCharArray();
        char[] t = T.toCharArray();
        while (i < s.length && j < t.length) {
            tag++;
            if (s[i] == t[j]) {
                i++;
                j++;
            } else {
                i = i - j + 1;
                j = 0; //一旦不等,j就從0開始
            }
        }


        System.out.println("迴圈了" + tag + "次");
        if (j == t.length) {
            return i - j;
        } else {
            return -1;
        }
    }


    /**
     * 使用kmp演算法的情況下
     * kmp原理,就是 每次匹配的時候,如果t[j]和s[i]不相等,j不是從零開始,而是從k開始,k的特點是 t的0到k-1個位置字元和j-1 到 j-k-1個字元相等
     * 比如說
     * ababd在和ababfababde 匹配的時候
     * ababfababde
     * ababd
     * 在j=4的時候不匹配時,也就是t[4]=d  不匹配的時候,j的下一個值不是0 ,而是2, 即t[2]=a ,而且此時i指標也不需要動
     * 此時比對場景為
     * ababfababde
     *   ababd     此時 在t[2]仍然不匹配  j的下一個值是0
     * <p>
     * ababfababde  此時t[0]時候 仍然不匹配 ,則i要向前移動1位
     *     ababd
     * <p>
     * <p>
     * ababfababde   此時匹配成功返回i
     *      ababd
     * <p>
     * <p>
     * <p>
     * 因此 這裡會發現,求j的下一個值 next[j] 等同於是在求 第j個字元前面的,最大相同字首和和字尾的長度
     * 比如說 ababf f前面的最大相同字首和字尾為ab 和ab  長度為2 因此next[4]=2
     * <p>
     * t=2的時候  也就是aba  第三個a前面的ab ,最大相同字首和字尾長度為0  因此next[2]=0
     * <p>
     * 最大字首: 包含第一個字元,不包含最後一個字元
     * 最大字尾:包含最後一個字元,不包含第一個字元
     * <p>
     * 最大相同前後綴,即字首和字尾相同的部分 最大長度的
     *
     * @param
     */
    public static int KMP(String S, String T) {
        int tag = 0;

        int[] next = next(T);
        int i = 0;
        int j = 0;
        next[0] = -1;

        char[] s = S.toCharArray();
        char[] t = T.toCharArray();
        while (i < s.length && j < t.length) {
            tag++;
            if (j == -1 || s[i] == t[j]) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }

        System.out.println("迴圈了" + tag + "次");
        if (j == t.length) {
            return i - j;
        } else {
            return -1;
        }
    }


    public static int[] next(String T) {
        char[] t = T.toCharArray();
        int[] next = new int[t.length];
        next[0] = -1;//-1表示不存在
        int i = 0;
        int j = 1;
        while (j < t.length) {
            if (i == -1 || t[i] == t[j]) {
                i++;
                next[j] = i;
                j++;
            } else {
                i = next[i];
            }
        }

        for (int i1 : next) {
            System.out.print(i1);
        }
        System.out.println();

        return next;
    }


    public static void main(String[] args) {
        System.out.println(index("abcdefabcdefabcdefabcdefabcabcabcabcdeh", "abcdeh")); //迴圈了68次
        System.out.println(index("abc", "bc")); //迴圈了3次
        next("abcabc");//-100123
        System.out.println(KMP("abc", "bc")); //迴圈了4次
        System.out.println(KMP("abcdefabcdefabcdefabcdefabcabcabcabcdeh", "abcdeh"));//迴圈了50次
        System.out.println(KMP("abcdeiopsfsfasdaffabcdefabcdefabcdefabcabcabcabcdehabcdes", "abcdeh"));//迴圈了74次

    }


}