KMP算法 求next数组的原理

来源:互联网 发布:火线精英免费刷枪软件 编辑:程序博客网 时间:2024/05/03 17:52
在kmp算法中,最难理解的那部分就是next数组的求解原理,今天看懂了一点点,现在记录一下

我们假设有这么一个字符串(模式串):abac......abab.......        
为方便起见,我们用数组 S 称呼这个字符串

下标0123................56789..............值abac................abab ..............  k1 k    jj+1 


如上图所示,我们可以知道:当 j=8 的时候,next[j]=3(因为S[0]S[1]S[2] = S[5]S[6]S[7]),现在我们来求next[j+1]

  1. 如果S[k] = S[j],即S[3] = S[8],  那么 next[9] = next[j+1] = next[j] + 1 = k + 1 = 4
  2. 如果S[k] != S[j],即如上图所示的S[3] != S[8],那该怎么办呢?    下面着重讨论这个问题
由表格中的数据我们可知,由于S[k] != S[j],所以next[j+1]不可能为k+1这么简单,也就是说 next[j+1] 的长度不可能比 next[j] 再长了,所以我们只能寻找更短的相同前后缀

现在我们让 k1 = next[k](本来在代码中应该是:k = next[k],从而构成一个循环,但这里为了方便描述故而采用 k1 的说法,千万不要被误导),由于 k = 3,所以新的 k1 = next[3] = 1(前提是 next[k] 真的有相同的前后缀)(因为S[0] = S[2]),下面重点来了:

因为 S[0]S[1]S[2] = S[5]S[6]S[7] , 所以S[2] = S[7], 又因为有 S[0] = S[2],所以 S[0] = S[7],此时如果有S[k1] = S[j],那么就有:S[0]S[k1] = S[7]S[j],那么就可推断出 next[j+1] = next[k] + 1

最后我们发现 S[k1] = S[1] = b,S[j] = S[8] = b, 所以S[k1] = S[j] , 所以next[j+1] = next[k] + 1  ==> next[9] = 2

(2)上面的情况是 S[k1] = S[j],所以才有 next[j+1] = next[k] + 1, 那要是 S[k1] != S[j] 呢?又会是个什么情况

下标0123................56789..............值abac................abad .............. k2k1 k    jj+1 
如上面表格所示:此时 k1 = 1, j = 8, S[k1]  !=  S[j]  即:b != d, 这个时候我们继续让 k2 = next[k1] = next[1] = 0, 由于S[k2] != S[j], 即:S[0]  != S[8], 所以继续让 k3 = next[k2] = next[0] = -1

这个时候 k3 = -1,说明 next[j+1] 没有相同的前后缀,所以我们就让 next[j+1] = 0,并且重新开始计算 next[j+2] 的相同前后缀,对应的代码就是:j++; k++; next[j] = k; (因为 k 自增之后才为0,j 自增刚好用于计算下一个字符的相同前后缀)


说明:我这里只是描述了其中一种情况而已,但是已经足够用来说明next数组的求解原理了,表格中的红色字体部分可以替换成更长的字符序列,都没有任何问题,因为原理都是一样的


下面是KMP算法的完整代码:

    /**     * KMP算法     * @param source 源字符串     * @param target 模式字符串     * @return 匹配到的第一个下标     */    public static int kmp(String source, String target) {        char[] s = source.toCharArray();        char[] t = target.toCharArray();        //计算next数组        int[] next = getNext(target);        int i = 0, j = 0;        while (i < s.length && j < t.length) {            if (j == -1 || s[i] == t[j]) {                i++;                j++;            } else {                j = next[j];            }        }        // 匹配成功,返回第一个匹配到的下标,否则返回-1        if (j == t.length) {            return i - j;        }        return -1;    }    /**     * 计算next数组     * @param target 模式串     * @return next数组     */    public static int[] getNext(String target) {        char[] t = target.toCharArray();        int[] next = new int[t.length];        //需要单独给next[0]赋值为-1,因为后面的循环最多只能给next[1]赋值,无法循环到next[0]        next[0] = -1;        int j = 0, k = -1;        while (j < t.length - 1) {            if (k == -1 || t[j] == t[k]) {                j++;                k++;                next[j] = k;            } else {                k = next[k];            }        }        return next;    }





0 0
原创粉丝点击