KMP算法的next数组是怎么算的

一:引言

这几天刚开始学算法,感觉还是很吃力的,比如今天学习的KMP算法,KMP算法的介绍我就不必多说,我们都知道此算法的核心就是前缀数组next的创建,而今天我在学习的时候,在这里遇到了问题,我在网上找了很多视频,但依然很懵,没有彻底透明。然后呢,我就反复的看视频的实现代码,用很多例子带入观察。最后,由于我脑子愚钝,花了一下午才弄明白到底是怎么回事。写此博客记录下成果,同时也给在学此算法的刚入门的小伙伴一个小提示。

二:详解

首先我们来看看 “部分匹配表(next)” 是怎么产生的。

先介绍前缀,后缀是什么

对于一个字符串来说,如单词“spark”来说:

字符串:spark

前缀:s,sp,spa,spar

后缀:park,ark,rk,k

 “部分匹配值”就是“前缀”和“后缀”的最长的共有元素的长度。以“ABABCABAA”为例:

“A”的前缀和后缀都为空寂,共有元素的长度为0;

“AB”的前缀为[A],后缀为[B],共有元素的长度为0;

“ABA”的前缀为[A,AB],后缀为[BA,A],共有元素的长度为1;

“ABAB”的前缀为[A,AB,ABA],后缀为[BAB,AB,B],共有元素的长度为2;

“ABABC”的前缀为[A,AB,ABA,ABAB],后缀为[BABC,ABC,BC,C],共有元素的长度为0;

“ABABCA”的前缀为[A,AB,ABA,ABAB,ABABC],后缀为[BABCA,ABCA,BCA,CA,A],共有元素的长度为1;

“ABABCAB”的前缀为[A,AB,ABA,ABAB,ABABC,ABABCA],后缀为[BABCAB,ABCAB,BCAB,CAB,AB,B],共有元素的长度为2;

“ABABCABA”的前缀为[A,AB,ABA,ABAB,ABABC,ABABCA,ABABCAB],后缀为[BABCABA,ABCABA,BCABA,CABA,ABA,B,A],共有元素的长度为3;

“ABABCABAA”的前缀为[A,AB,ABA,ABAB,ABABC,ABABCA,ABABCAB,ABABCABA],后缀为[BABCABAA,ABCABAA,BCABAA,CABAA,ABAA,BA,AA,A],共有元素的长度为1;

 由此可见,“ABABCABAA”的next数组为[0,0,1,2,0,1,2,3,1]。当然,这是我们人眼观察的结果,但是程序怎么写呢?先来看一张图:

如图所示, 假设字符串为char型数组T[],部分匹配值为next[]数组。我门以第9个元素也就是T[8]的时候为例。

我们先声明一个变量j,我们知道,当j为(0,1,2),i为(5,6,7)的时候,一直是相等的,然后i++到8,j++到3之后,就不相等了。我刚开始的想法就是,只要不相等,置为0就可以了,但从上图来看,明显i=8的时候,对应的值为1,所以没有那么简单。

我们可以先看下当前j值为3时的前一个也就是next[ j-1 ](next[2])的值,此图中为1,则说明一定是T[0] == T[2]。然而我们既然比较到了T[3]和T[8],说明一定T[2] == T[7],那由此可见当next[2]为1的时候,T[0] == T[7],所以我们应该比较的是T[1]是否等于T[8]。为以下几种:

当T[1] == T[8] 时,return值为2;

当T[1] != T[8] 时,比较T[0] ?= T[8];

如果T[0] == T[8],return值为1,否则return值为0。

我们再来举个例子,如果next[j - 1] == 2,那么说明数组T[]的前两个元素T[0],T[1]分别与T[j-2]和T[j-1]相等,相同的我们比较到了j和i的位置,说明T[j-2] == T[i-2] 且 T[j-1] == T[i-1]。由此得出T[0] == T[i-2],T[1] == T[i-1],那我们首先要比较的就是T[2]和T[i]是否相等,如果相等返回3,如果不相等继续比较T[0]和T[i],如果相等返回1,否则返回0。

我们可以用公式 j = next[j-1] 来设置j,如图中,当j==3和i==8的时候,不相等,如上分析的那样我们会去找j==3的前一个元素对应的next的值,如果next[j-1] == 1,则比较第2个元素T[1],此时恰好为T[ next[j-1] ],因此要把next[j-1]赋值给j,然后循环,直到j的值为0。

如果还是一知半解,带入next[j-1] == 2的例子试试就好了。

附上此处代码:

    public static int[] kmpNext(String dest){
        int[] next = new int[dest.length()];
        next[0] = 0;
        for (int i = 1,j = 0; i < dest.length(); i++) {
            while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
                j = next[j -1];
            }

            if (dest.charAt(i) == dest.charAt(j)){
                j++;
                next[i] = j;
            }
        }
        return next;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值