字符换匹配算法-KMP算法

活动地址:CSDN21天学习挑战赛

归结起来,焦虑的原因就两条:想同时做很多事,又想立即看到效果.一步一个脚印,加油!

KMP算法的核心思想与BM算法相近。其不同之处在于,在模式串与主串进行匹配的过程中,当遇到不可匹配的字符时,我们希望找到一些规律,将模式串直接后移几位,跳过那些肯定不会匹配的情况,而不是从新开始。

那么如何确定跳过几个字符呢?这就需要借助我们计算出一个 next 数组,在 next 数组中存储这其前面所有字符的最长公共前后缀长度,如abcabd 的next数组为 [-1, 0, 1, 0, 1, 2],我们先不去纠结这个 next 数组是如何计算的,现在假设我们有了这个 next 数组,那么我们应该怎么去使用?

public int indexOf(String text, String pattern) {
    int tLen = text.length(); int pLen = pattern.length();
    if (tLen == 0 || pLen == 0 || pLen > tLen) return -1;
    // 1. 初始化next数组
    int next[] = initNext(String pattern);
    int tIndex = 0; int pIndex = 0;
    while (tIndex < tLen && pIndex < pLen) {
        // 2. pIndex = -1 时tIndex和pIndex同时前进
        if (pIndex = -1  || text.charAt(tIndex) == pattern.charAt(pIndex)) {
            tIndex++;
            pIndex++;
        } else {
            // 3. 通过next数组越过不必要的比较
            pIndex = next[pIndex];
        }
    }
    if (pIndex == pLen) return tIndex - tLen;
    return -1;
}

如上,我们对之前的BF算法进行简单的三处修改就完成了KMP算法,我们先来通过第3处修改来看一下next数组的作用,现在假设我们匹配的主串和原始串分别为和cdaabaabaagdaabaag如下,那么当我们匹配到一下情况时。
字符换匹配算法-KMP算法_第1张图片

假如,我们使用是BF算法,很显然箭头又要重新回到最起始的位置,而主串下标指针前进一位
字符换匹配算法-KMP算法_第2张图片
而假如我们使用了next数组,结果就会是下面这种情况
字符换匹配算法-KMP算法_第3张图片
可以明显的看到通过next数组,我们省去了很多不必要的比较,在代码中的的表达就是pIndex = next[pIndex];

知道了next数组的作用之后,我们再来说一下为什么可以直接使用最长公共前后缀长度进行赋值。首先,如果前后缀相,那么pIndex = next[pIndex]就相当于移动了最长公共前后缀长度,显然是拿模式串的前缀与主串的后缀进行匹配,那么为什么是最长,这是因为如果不是最长就会有遗落的情况。而-1则是为了应对开始没有公共前后缀的情况,也就是主串向前移动一位,模式串由-1变为0.

最后我们再来看一下next数组如何计算

private int[] initNext(String pattern) {
    int pLen = pattern.length();
    int[] next = new int[pLen];
    int n = next[0] = -1;
    for (int i = 0; i < pLen - 1; i++) {
        if (n < 0 || pattern.charAt(i) == pattern.charAt(n)) {
            next[++i] = ++n;
        } else {
            n = next[n];
        }
    }
    return next;
}

其中pattern.charAt(n)就是当前最长前缀的下一个字符,其核心思想就是如果当前最长前缀的下一个字符和当前字符相同,那么就可以在当前最长前缀的长度上加1即可,否则就将当前最长前缀(当前最长前缀下标对应的next数组值)。

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