KMP算法之next数组的构建

前面一篇文章描述了KMP算法的思想和next的数组的由来,而这篇主要讲解next数组的构建。此文章沿用了上一篇的数组名称,主串为S,模式串为P。

这边在放一下前面一篇文章的链接KMP算法之基础思想篇

首先next[0] = -1 这个初始化,上篇文章已经讲了,具体可以看上篇。

上图标注②的初始化,为什么是i为0,j为-1呢,j实际上代表的是next的值,i表示的是next的下标,前一步有next[0]=-1,初始化自然也就是i为0,j为-1。

对于③next[++i] = ++j,这里就要回想下,当前位的next值是怎么求出来的。当前位的next值,是根据前一位的next值和前一位两个比较字符是否相等得出来的,换句话说,下一位的next值,是根据当前位的next值和当前比较的两个字符是否相等 得出来的,这里比较绕,可以结合例子来看,假设i=5,j=4,next[i] = 3,那么如果P[i] == P[j],(不相等的情况下面写),则证明了公共部分是可以延长一位的,所以下一位next[i+1]自然就是next[i]+1 = 4,next[i]是用来代表模式串P前面i-1位前后缀最长公共部分的值,而当前P[i]和P[j]位的判断,关系的是next[i+1]的值。这里一定要搞清楚,当前下标为i 和 j的字符比较,不是为了求next[i],而是为了求next[i+1],所以这也是为什么while的条件只到了 length - 1,而不是到length。

第一次j必为-1,则有next[1]=0,其实这个也是个定律,所有的next[1]都必为0的,因为无论是P[0] 和 P[1] 相不相等,只要P[1]和S[i]不相等,下一步都是P[0]和S[i]进行比较,毕竟P[0]自身根本构不成前后缀。

然后就是④ j=next[j],这个可能是大多数人觉得最难的地方,但我感觉最难的地方是③,如果③理解没问题,④其实是个自然而然的事情。

之所以能到④就是因为出现了j!=-1且两个字符比较不相等了。前面有讲相等的时候直接加一,那不相等的时候呢?还是要回归到next数组的含义上面,这边语言是在是难描述,也可能懂得不彻底所以描述不出来,还是画图说明把。

假设我们已知next[7]=3,next[15]=7,求next[16]=?

这里其实next[15] next[7]等于几都可以,我这边举一个相对可以多看几步的7。

next[15]=7代表P[0]-P[15] 有长度为7的前后缀公共部分,也就是红绿色标注部分,如果此时模式串P[7]和P[15]相等,那么next[16] = next[15]+1 = 8,如果不相等,那么是不是应该看一下红绿色任一部分中的前后缀公共部分呢?因为红绿色部分是相同的,所以如果红绿色任一部分中有相同的前后缀公共部分,那么还是可以做批量移动的。那这个时候怎么看红绿色部分中的前后缀公共部分呢?自然是去看next[7]。发现为3

同理,3的含义为,P[0]-P[7]的最长前后缀公共部分长度为3,那么,红色和绿色部分又是相同的,是不是可以得到。下图中4部分蓝色都是相同的。

此时接着比较P[15]和P[3],如果P[15]和P[3]相等,P[16]自然就为P[3]+1 = 4。如果不相等呢?一样的看next[3]的值就好了,我们在假设next[3]的值为1,

证明只有长度为1的公共前后缀部分,同理,既然4个蓝色部分都是相同的,可得到下图的黄色部分

其实我们只需要关注,P[0]和P[14]是相同的即可了,如果此时P[15]和P[3]相等,那公共前后缀最长部分只有1了,那么next[16] = next[3] + 1 = 2。如果不相等,就只能接着找next[1]了。而next[1]又肯定是0(前面有说),P[0]要是和P[15]相同,那next[16]就只能1,如果不相同,再找next[0],next[0]肯定为-1,这个时候就已经可以结束了,因为已经到第一位了,不可能有公共前后缀了,此时next[16]为0,表示要从头开始匹配了,这就是最不理想的情况了。

至此我相信有很多小伙伴都可以明白为什么要j=next[j]了,就是因为要去不断的判断可不可以匹配到相同部分(红绿)的中的子(蓝)最长的公共前后缀,这个子的概念就是比如上图中的蓝色就是红绿色的子,黄色是蓝色的子。一旦可以匹配到,就直接+1就好了,否则直到匹配到头,就是-1,也就是最坏的情况结束,下一位的next值只能是0了。

OK,到了这里,KMP算法基本也就结束了,既然next数组都已经求出来了,那么如何判断S中有P呢,只需要按照next的规则,依次判断字符是否相等,相等则往下继续,不相等则让P按照next数组来移动,最终如果代表P下标的j,大小和P的长度一致了,那么自然就可以得出P是S的子串咯。

最后的最后,附一个力扣28题的代码把,如果换个问法,让求S中是否有P,那么return的值换一下就好拉。KMP算法就到这里拉。如果对此文章有疑问或者有其他见解的同学,欢迎评论留言呀。当然文章如果有写的不对的地方,也非常期待指正哟~~~~

public int strStr(String haystack, String needle) {
        if (needle.length() == 0){
            return 0;
        }
        int[] next = new int[needle.length()];
        getNext(needle, next);
        int i = 0, j = 0;
        while(i < haystack.length() && j < needle.length()) {
            if (j == -1 || haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
            if (j == needle.length()) {
                return i - j;
            }
        }
        return -1;
    }

    public void getNext(String p, int[] next){
        next[0] = -1;
        int i = 0, j = -1;
        while (i < p.length() - 1) {
            if (j == -1 || p.charAt(i) == p.charAt(j)) {
                next[++i] = ++j;
            } else {
                j = next[j];
            }
        }
    }

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值