【算法4】5.3.3.子字符串查找-KMP算法

KMP 是发明此算法的三个人名字首字母。(Knuth, Morris, Pratt)

暴力查找每次匹配失败时都需要回退指针 i,并重新对比已经检查过的字符。
KMP 通过对模式字符串进行预处理,生成一个确定有限状态自动机,
将文本字符串的字符依次放到自动机中,可以直接得到需要和文本字符串下一个字符 text.chatAt(i+1) 匹配的 j 的位置 pattern.chatAt(j)
即 j 应该向前移动一个位置(匹配的情况)或将 j 重置到之前的某个位置(不匹配的情况)。

可全屏查看代码

/**
 * KMP 算法
 * */
public class KMP {
    private static final int R = 256; // 字母表
    private int dfa[][]; // 确定有限状态自动机
    private int M;

    /**
     * 对模式字符串进行预处理,生成确定有限状态自动机 dfa
     * */
    public KMP(String pattern) {
        // 当匹配失败时,我们可以知道当前匹配的子串是 pattern[i-j, j],接下来需要匹配的子串是从 pattern[i-j+1] 开始
        // 所以我们知道接下要放入自动机的字符,而将字符放入自动机又可以得到 j 的新值
        // 所以在生成 dfa 的同时也在将模式字符串输入到自动机中,以此更新 X 的值
        // dfa 的第一个索引表示的是输入的字符 c,第二个索引表示当前模式字符串的位置 j,值表示下一个状态(即需要将 j 重置到哪个位置)
        M = pattern.length();
        dfa = new int[R][M];
        // 初始化自动机的状态,第一个字符匹配成功进入下一个状态,否则保持不动
        dfa[pattern.charAt(0)][0] = 1;
        // 从这里开始一边生成自动机,一边使用自动机
        int X = 0; // 记录使用自动机的当前状态
        for (int j = 1; j < M; j++) { // 在生成自动机时,从第二个字符开始使用自动机
            for (int c = 0; c < R; c++) {
                dfa[c][j] = dfa[c][X]; // 枚举字符集,并将每个字符放入自动机
            }
            dfa[pattern.charAt(j)][j] = j + 1; // 匹配成功进入下一个状态
            X = dfa[pattern.charAt(j)][X]; // 将当前字符放入自动机,更新状态 X
        }
    }

    /**
     * 在文本中查找模式字符串
     * */
    public int search(String text) {
        int N = text.length();
        int i, j;
        for (i = 0, j = 0; i < N && j < M; i++) {
            j = dfa[text.charAt(i)][j];
        }
        if (j == M) {
            return i - j;
        } else {
            return -1;
        }
    }
}

参阅

posted @ 2022-06-03 17:22  廖子博  阅读(63)  评论(0)    收藏  举报