(自用)KMP算法中next数组的求解算法

看到站内一篇讲的很明白的文章,本着“能给别人讲明白=自己真正理解”的原则,试着写一篇讲解加深自己的理解

原文:KMP 算法中的 next 数组推导(图解 + 代码实现)_kmp算法next数组-优快云博客

next数组:

对于字符串S中第j个元素,设其前面的元素组成的子串为S',则next[j]等于S’中重合的前缀和后缀的最大长度。

前缀:字符串中包含首字符且不包含尾字符的任意子串

后缀:字符串中包含尾字符且不包含首字符的任意子串

数学表述如下(这是元素下标以1为起始的情况,以0为起始的话都-1就可以了,后面我为了和看的原文一致采用下标0起始

可以得到next数组值的(除了表示子串和主串失配时子串指针应指向的位置)另一个含义:

如果next[j]=k,则表示j前面有k个元素,可以与S中长度为k的前缀相同。也即下图中浅色部分相同)阿达瓦https://i-blog.csdnimg.cn/blog_migrate/21533f0366919bc2886456cdce0cd583.png

S[j]=S[k]的情况 

这是最简单的情况,如果S[j]=S[next[j]]=S[k],则对于j+1,也满足了上述条件,即next[j+1]=k+1=next[j]+1

 S[j]!=S[k]的情况

这种情况最麻烦,也最难以理解。

考察next值的含义,我们可以得到,要求得next[j+1],就是要找一个k1,使得S[k1]=S[j]且next[j]=k1,也即下图中浅蓝色的区域要相同

找到k1=j比较容易,但是保证前面的部分相同有困难。考虑到k=next[j],也即S[0,k-1]=S[j-k,j-1],总能在k前面0找到一段长度为k1的部分,使得这部分可以与j前面长度为k1的部分重合(即下图的2和3,注意,此时2,3长度都小于k)。此时问题就转化为了找到一个k1使得1,2相同。

很显然,根据next数组的意义,此时k1=next[k]=next[next[j]]。 问题又回到了上面这样的形式。阿达瓦如此一层层递归下去。要么能找到符合条件的k,要么一直找不到。由于k是“顺着”next的值回溯,而next[0]=-1,next[1]=0是确定的。如果一直找不到的话,最后k势必会回溯到-1,这表示j+1前面已经不存在能够重合的前后缀了,next[j+1]=0

代码部分

void getNext(const string& s, int next[]) {
    int len = s.size();
    int j = 0;         // 表示当前要求的 next[i]
    next[0] = -1;
    int k = -1;        // 记录上一个 next
    while (j < len - 1) {
        if (k == -1 || s[j] == s[k]) {
            next[++j] = ++k;
        } else {
            k = next[k];
        }
    }
}

用一个变量k来维护next的值。next[0]=-1是确定的。当s[next[j]]=s[k]=s[j]时,更新next[j+1]=k+1,否则,k顺着next值回溯,直到找到s[k]=s[j]或者到达边界条件k=-1,由于到达边界条件时,k=-1,next[j+1]=0=k+1,且初始条件为j=0,k=-1,下一步j=1时next[1]=0也是确定的。所以这几种情况可以合并。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值