kmp算法的next数组解析

昨天天杀的 做了个字符串匹配的题,于是天杀的 重新学习 kmp算法(实际上第一次学的时候就没搞懂 )。

首先搞清楚几个概念:①什么是字符串匹配?比如给定字符串"aabcaa",想要知道"abca"是否存在于该字符串中,这个过程就是字符串匹配。类似于String.indexOf()。②什么是kmp算法?如果暴力破解,“abca”在匹配的过程中,先会匹配a,然后是b,如不同,就将"aabcaa"的指针向后移一位,再重新匹配。kmp使用next数组,将重新匹配的过程减少,使得效率大大提升。next数组只与“abca”有关。

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例 1:

输入: “abab”

输出: True

解释: 可由子字符串 “ab” 重复两次构成。 示例 2:

输入: “aba”

输出: False 示例 3:

输入: “abcabcabcabc”

输出: True

解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)

先放代码:

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        return kmp(s + s, s, 1) != s.length();
    }

    private int kmp(String s1, String s2, int p) {
        char[] s = s1.toCharArray();
        char[] t = s2.toCharArray();
        int n1 = s.length, n2 = t.length;
        int[] next = getNext(t);
        //i是s当前需要比较的字符,j是t当前比较的字符,也是前后缀相同字符的个数
        int i = 0, j = 0;
        while (i < n1 && j <= n2) {
            if (j == n2) {
                if (p == 0) break;
                else {
                    p--;
                    j = next[j];
                    continue;
                }
            }
            if (j == -1 || s[i] == t[j]) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == n2) {
            return i - j;
        }
        return -1;
    }

    private int[] getNext(char[] t) {
        int[] next = new int[t.length + 1];
        //k的含义是:前j个字符中,有k个是前后缀相同的。
        int j = 1, k = 0;
        next[0] = -1;
        while (j < t.length) {
            if (k == -1 || t[k] == t[j]) {
                next[++j] = ++k;
            } else {
                //此时左边k的含义是:前k个字符中,有next[k]个是前后缀相同的。
                //取得的k的可以用在t[k] == t[j]时,表示前j个字符中,有k + 1(1就是第j位)个前后缀相同
                k = next[k];
            }
        }
        return next;
    }
}

最关键的还是next数组,次关键的是next数组的应用。

next数组

next数组的值是最长公共前后缀,在第 j 的位置(j 是长度),有k个字符(即前缀)与后k个字符(即后缀)相同。

比如下图 next[6] = 2 是因为前6个字符中的前缀(前两个"abxxxx" )与后缀(后两个 “xxxxab” )相同。

在这里插入图片描述
测试一下理解:上图中,不看已给结果,算一下next[8]是多少?

next数组如何生成?
在这里插入图片描述

怎么编码呢?

next数组的计算,以这张图辅以理解,想象出一个双指针 j 和 k ,j 是第 j 个字符,用在t[j] 和 next[++j] (t数组声明在代码),表示着当前正在比较的字符。k 是前后缀相同字符的个数,k 总是小于 j 的。每次 t [ k ] 与 t [ j ] 进行比较时,如果相同,那就让 next [ ++j ] = ++k (因为next是长度为下标所以要+1) 。如果不相同,就让 k = next[k] ,这个的含义在代码中写了,它的目的是减少重复比较,直接得出前后缀有多少个是相同的。

那这个next[0] = -1 的含义呢?其实他是不是-1根本无所谓,只要是负数就行,但不能是非负数因为会和next数组的其他项重复。-1就是一个标志,他标志着在 j 这个位置的所有前面的字符都没有前后缀,也标志着第0位与第 j 位不相等。

为什么设置这个标志位?先看为什么 k 会等于-1,k会等于-1就是上一次 k = 0 ,这又是因为上一次第 j 位与第 0 位不相等,既然已经不相等了,那就需要让 j++,让next[j] = 0,让 k = 0。设置了这个标志位,就是设置了循环终止符,循环是k = next[k];所产生的循环。如果不设置这个标志位next[0],就不能确定这次是比较还是让 j++。

用上图给出的字符串在脑海中测试一下,看看自己理解的k = next[k];有没有问题,如果有问题,你会在next[8]处写上0(我之前就是

next数组的应用

现在有主串S:ababcabcacbab,模式串T:abcac。i从0开始,j也从0开始。
根据上述方法可以知道,T的中每个字符对应的失效函数值为:
(将表中公共前后缀最长长度全部右移一位,第一个值赋为-1)在这里插入图片描述
第一次匹配中,i从0开始,j从0开始。当i=2,j=2时匹配失败,此时i不动,next[j]=next[2]=0,接下来模式串T要相对于主串S向右移动j - next [j] = 2 位,j回溯到0。
在这里插入图片描述第二次匹配中,i从2开始,j从0开始。当i=6,j=4时匹配失败,此时i不动,next[j]=next[4]=1,接下来模式串T要相对于主串S向右移动j - next [j] = 3位,j回溯到1。
在这里插入图片描述第三次匹配中,i从6开始,j从1开始。当i=10,j=5时匹配成功,返回i - j = 5。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值