字符串匹配之KMP算法

KMP算法的难点在于next数组和前缀表
KMP算法流程:

  • 假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置
  • 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j] ),都令 i++,j++,继续匹配下一个字符;
  • 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j] ),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P相对于文本串 S 向右移动了 j - next [j] 位
    换言之,将模式串 P 失配位置的 next 数组的值对应的模式串 P 的索引位置移动到失配处
    字符串匹配之KMP算法_第1张图片
    字符串匹配之KMP算法_第2张图片
    首先,列出模式串 P 的所有子串:
    字符串匹配之KMP算法_第3张图片

然后,求得每一个子串的所有前缀与后缀。

前缀 指除了最后一个字符以外,一个字符串的全部头部组合;后缀 指除了第一个字符以外,一个字符串的全部尾部组合。

以子串abaab为例:
字符串匹配之KMP算法_第4张图片
因此,它的前缀后缀的公共元素的最大长度为 2

求得原模式串 P 的子串对应的各个前缀后缀的公共元素的 最大长度表 下图。
字符串匹配之KMP算法_第5张图片
根据最大长度表 去求 next 数组:next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。
字符串匹配之KMP算法_第6张图片
字符串匹配之KMP算法_第7张图片
Java实现

private int[] getNext(String p) {
        int M = p.length();
        int[] next = new int[M];
        next[0] = -1;
        int j = 0;
        int k = -1;
        while (j < M - 1) {
            if (k == -1 || p.charAt(k) == p.charAt(j)) {
                next[++j] = ++k;
            } else {
                k = next[k];
            }
        }
        return next;
    }
    int KmpSearch(String p, String t) {
        // 根据模式字符串获得next数组
        int[] next = getNext(p);
        int N = t.length();
        int M = p.length();
        int i = 0;
        int j = 0;
        while (i < N && j < M) {
            if (j == -1 || p.charAt(j) == t.charAt(i)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == M) {
            return i - j;
        } else {
            return -1;
        }
    }

你可能感兴趣的:(数据结构和算法)