KMP算法的next数组计算

文章详细介绍了KMP算法中的核心部分——next数组的含义和计算方法。next数组用于在字符串匹配过程中确定游标的回退位置,避免重复比较。当字符相等时,next[j+1]=next[j]+1;不等时,通过寻找新的k1使得next[j+1]=next[next[j]]+1,可能存在多层嵌套。最后,文章通过一个实例展示了next数组的计算过程。
摘要由CSDN通过智能技术生成

next数组的意思

在KMP算法中,目标字符串的游标不可能回退,只有用于匹配的字符串的游标会回退,而回退的位置却不是任意的,它通过next数组来获取下一次回退的位置。个人认为,对于next数组的理解和计算是整个KMP算法的核心 。

对于某一个字符串,关于next数组的解释如下:
在这里插入图片描述
在上面有 next[j]=k ,表明 k 前面的浅蓝色区域和 j 前面的浅蓝色区域是相同的。(不包括k,j
在这里插入图片描述
上图给出的则是当字符串 j 位置所在的字符和主串对应的字符不相等时,j 回退到 k ,在继续和主串对应的字符进行匹配。这样字符串 k 前面的字符就不需要比较了,有效的解决了暴力匹配中重复计算的问题。

next数组的推导和计算

首先,对于字符串的第一个字符,我们约定:next[0] = -1不同的教程可能是将next数组的下标从1开始计算,并记 next[1] = 0,这个无伤大雅,基本原理摆在这里

现在,我需要去求next[j+1],而目前已知 next[j] = k 。则如下图:
在这里插入图片描述
此时,需要考虑的是 s[k] 和 s[j] 是否相等,下面给出讨论:

当 s[k] == s[j] 时

则应当有 next[j+1] = next [j] + 1,由上图显而易见可以得出的结论

当 s[k] != s[j] 时

则不能像等于的情况下一样,简单的得出结论,我们需要找到一个新的k1,满足 k1 前的子串和 j 前的子串是相等的。注意到 k1 一定小于 k ,毕竟s [k] 已经不符合条件了。
在这里插入图片描述
再上图中有 s[k1] == s[j],那么则有next[j+1] = next [k1] +1。现在问题来到了怎么找到K1:

注意到下标 k 前面存在一段子串和下标 j 前面的一段子串完全相同。并且下标 k 前的子串是一定长于下标 k1 前面的子串(k1 < k),那么在下标 k 前的子串中,一定能找出一个子子串和 k1 前的子串相等。

在这里插入图片描述
在上图中,1 和 2 和 3 是完全相等的子串。由于1 和 2 已经相等了,那么有关系 next[k] = k1带入上面的式子中,则有 next[j+1]=next[next[j]] + 1

此时还有一个问题,如果不存在这样的 k1 怎么办,那么最终肯定有的k1 = -1,就是从头开始匹配。此外,注意到不一定是只嵌套一层next,因为上面的结论是成立在存在有的前提上,它可能需要经过多次嵌套才能得出结果。

一个例子

游标01234567
字符ababaaab
next-10012311

对于 next[0], 规定为 -1,根据前面的分析:next[1] = 0;

对于 next[2],则观察 1 和 next[1] = 0,即 b 和 a,不相等;而next[next[1]] = -1,因此 next[2] = 0;

对于 next[3],观察 2 和 next[2] = 0,即 a 和 a,相等,故 next[3] = next[2] + 1 = 1;

对于 next[4],观察 3 和 next[3] = 1,即 b 和 b,相等,故 next[4] = next[3] + 1 = 2;

对于 next[5],观察 4 和 next[4] = 2,即 a 和 a,相等,故 next[5] = next[4] + 1 = 3;

对于 next[6],观察 5 和 next[5] = 3,即 a 和 b,不相等,next[next[5]] = 1,与 5 比较即 b 和 a,不相等,继续递归 next[next[next[5]]] = next[next[3]] = next[1] = 0;比较 0 和 5 即 a 和 a,相等,因此 next[6] = next[next[next[5]] + 1 = 1;

对于 next[7],观察 6 和 next[6] = 1,即 a 和 b,不相等,next[next[6]] = 0,0 和 6相等,因此 next[7] = next[next[6]] + 1 = next[1] + 1 = 1;

代码实现

#include<bits/stdc++.h>
using namespace std;
class KMP {
private:
    string target;
    string str;
    int m;
    int n;
    int* next;
    void get_next(){
    // 用next数组来并保存下次开始匹配的位置
        next = new int[m];
        next[0] = -1;

        int i = 0;
        int j = -1;

        while(i < m){
            if(j == -1 || str[i] == str[j]){
                i++;
                j++;
                next[i] =j;
            }else{
                j = next[j];
            }
        }

    }
public:
    KMP(string target,string str);
    int find();
};
KMP::KMP(string target, string str){
    this->target = target;
    this->str = str;
    this->n = target.size();
    this->m = str.size();
    get_next();
}
int KMP::find(){
    int i = 0;
    int j = 0;
    while(i<n&&j<m){
        if(j==-1||target[i]==str[j]){
            i++;
            j++;
        }else{
            j=next[j];
        }
    }
    if(j==m){//遍历完str
        return i-m;
    }else{
        return -1;
    }
}
int main(){
    KMP test("ababaaacdefababaaabg","ababaaab");
    cout<< test.find()<<endl;
}

图片和部分思路借鉴:KMP 算法中的 next 数组

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值