KMP算法中对next数组的自我理解(C++实现)

KMP算法中对next数组的自我理解(C++实现)

因为疫情居家,有着充足的时间来进行对数据结构与算法的自主学习,在学到KMP算法这段的时候,对于next数组的求解一直是很不明白,在网上查找了许多的博客和文章后,再加上自已一遍又一遍的比划,算是终于想明白了,在此与大家分享自己的理解。

首先,先说一下这个next数组最后求出来以后的数据到底是什么意思。

举个例子

stringababaaaba
next-100123112

a[0]为-1,表示这里已经是开头,不能再继续往前回溯了。

其余的每一个值,有两种理解。

第一种是指,在后面进行与主串的对比的时候,如果子串在该位置出错,下一次应该从哪一位继续进行比较。
比如在第5位(从0开始数)的a对比出现错误,发现next[5]为3,那我们就应该回溯到第3位(从0开始数),也就是从b继续对比。

第二种含义是指这个字符串中,截止这一个字符的前面的字符串,其最长的与后缀相等的前缀长度为多少。(好像有点绕hhhh,但一定要搞明白这是什么意思,后面会反复出现)
还是以刚才的第5位a(从0开始数)为例。截止这个a的前面的字符串为ababa,我们可以看到,这个字符串最长的与后缀相等的前缀为aba,其长度为3,所以next[5]就是3。

然后讲一下这个next数组的求解过程

先上代码

void getnext(string s, vector<int> &next)
{
    int i = 0, j = -1;
    next[0] = -1;
    while (i < s.length() - 1)
    {
        if (j == -1 || s.at(i) == s.at(j))
        {
            i++;
            j++;
            next[i] = j;
        }
        else
            j = next[j];
    }
}
在求解过程中,分三种情况:
第一种最特殊,就是next[0]。

这里我们标记第0位是整个字符串的最开头,再加上为了后续计算的方便,我们令next[0]=-1。

第二种情况就是s[i]=s[j]或者j=-1的情况。我们可以看到,这里的j是指前缀匹配到第几位。

(1)当j==-1的时候,说明前缀已经回溯到最开头了,他已经退无可退了,也就是说从0到i这段子串中,没有前后缀相同的部分,这段子串已经检查完了,所以我们让i往后进一位,然后让j也进一位(-1+1=0,让它从零开始),检查从0到i+1这一段。

(2)上面说过,这里的j是指前缀匹配到第几位,所以,当匹配到j的时候,从0到j-1这段子串,是与从i-[(j-1)+1]到i-1这段子串完全相同的,这段长度是j,此时这里是s[i]==s[j],那么这段匹配的子串长度会比刚才长1(这个好理解吧,原来有连续的3个字符匹配,现在连续的又有一个匹配,那不就是4个了吗),所以我们给next[i+1]赋值为j+1,也就是说明第i+1个字符前所有字符组成的子串,其最长的与后缀相等的前缀长度为j+1。所以就有了i++;j++;next[i]=j;的操作。

第三种情况就是s[i]!=s[j]的情况。

这个情况想了我好久,一直卡在j回溯的地方,网上只说了回溯,但具体为什么回溯感觉也没说明白。这里说说自己的理解,如果有不对的地方,希望大神们能指正。回归正题,刚才说过,在s[i]和s[j]比较前,前面已经有j长度的字符串是匹配了的,我在对next数组的理解中也写到,这个是最长的与后缀相等的前缀长度,那既然第j+1个字符不相等,那就说明这个最长的匹配字符串断掉了,如果还有匹配的字符串的话,它一定是比j要短的。那我们接下来的目的就是找有没有稍微短一点但依然匹配的前后缀。我们仔细观察,截止第i个字符前面的字符串,前j个字符和后j个字符是完全一样的,这前j个字符组成的字符串,其前缀等于后j个字符组成的字符串的前缀,同理,这前j个字符组成的字符串,其后缀等于后j个字符组成的字符串的后缀。那这样我们只要找到截止到j的最长的匹配的前后缀,那他自然也就能与尾部字符串的后缀相匹配。而我们又知道,截止到第j个字符的前面的字符串,其最长的与后缀相等的前缀长度为next[j],也就是下一次对比应该从第next[j]位开始对比,所以我们让j回到next[j],再次进行比对,一直找到其匹配的前后缀为止。

综上所述,我们就能得到上面的代码。

next数组是KMP算法的核心,能够想明白next数组的原理,相信KMP算法你也就能理解的差不多了。

这篇博文是纯粹我自己的理解,如果存在问题,希望各路大神能够进行指正,我必虚心改正。另外,希望大家能够提出自己的意见和建议,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值