KMP算法--next数组的构建

参考文章——卡尔大佬的B站视频,个人觉得部分讲解不够细致,作补充说明:我会按照代码将流程走一遍,把我觉得难理解的地方用自己的方式解释一遍,不恰当的地方欢迎指正。

void getNext(int* next, const string& s){
    int j = 0;
    next[0] = 0;
    for(int i = 1; i < s.size(); i++) { // 注意i从1开始
        while (j >= 0 && s[i] != s[j]) { // 前后缀不相同了
            j = next[j - 1]; // 向前回退
        }
        if (s[i] == s[j]) { // 找到相同的前后缀
            j++;
        }
        next[i] = j; // 将j(前缀的长度)赋给next[i]
    }
}

1. 首先定义:i —— 前缀表的尾; j —— 前缀表的前缀的尾; next[0] 默认是为0

这三点很简单理解。

2. 开始循环:前缀表为【a,a】当前缀a与后缀a相同,i和j都后移一位,与此同时会更新next

*需要注意的第一点就是j的后移是需要我们自己写的j++,然后跳出本轮循环,i是在循环中后移

 3. i后移之后,又因为i代表前缀表的末尾,所以此时的前缀表为【a,a,b】,我们的目的是寻找该表中的最长相同前后缀。此时比较的是【a,a】和【a,b】:

4. while循环中,此时s[i] != s[j],所以j要回退,回退到:前一位的最长相同前后缀(此时为0),从而比较更小的前缀【a】和后缀【b】:

*这一步回退我觉得是这部分代码的精髓,后面我将详细解释。

5. 此时j已经退到了下标为0的位置,不再进入while,更新next,再进入下一轮for循环:i++

6. 进入循环,判断值相同,则i和j都前移一位,并且更新next填入j(j++ 后为1)

7. 重复6,

 

 8. 判断i和j不同,开始进入while循环,选择最长相同前后缀,

目前是【a,a,b】和【a,a,f】,

 

 9. 开始回退,这一步回退我觉得比较难理解,实现的操作是【a,a】和【a,f】这两个前后缀作比较,但是代码比较巧妙,并不是简单的将j后退一格而已,而是“ j = next[ j - 1 ] ”,j跳转到了前一位的最长相同前后缀值,也就是本例中下标为1的位置。

 

 我加一个例子,如果是下图,就不是回退一格比较【a,b】和【b,f】了,而是j直接跳转到下标为0的位置上。从而next数组赋下一个值为0.

最长相同前后缀意味着与前缀相同元素的个数,也就是可以跳过的个数。

我们在之前的循环中已经走到了比较【aba】和【abf】的地步,意味着前面两位肯定都是对应的上的。此时表【a,b】中的这个b在next中为0,意味着和之前位置没有相同的值,也就是后面表【b,f】的b也和前表【a,b】中b的之前位置没有相同的值,而后面【b,f】中的b刚好是要和前面【a,b】中的a位置是对上的,所以前面一位都不同了,f说我还比个p啊,直接给我个0算了呗。

 再加个例子,若是把f换成a:【a,a,b,a,a,a】,最后的a说,既然前面的都相同了,那我就有了比较的意义,要是再相同就加1,更新next。不同就和我们最初的例子一样,再次让j回退。

最后代码上需要注意的是,虽然我们习惯性的先判断相同情况,判断不同情况。

但是代码中while要在if的前面,因为一旦先判断了相等的情况,j就会前移一位,从而影响了while中j的指向,造成错误。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
KMP算法是一种用于字符串匹配的高效算法,其中的next数组是该算法的核心部分之一。next数组用于记录模式串中每个位置的最长公共前缀和最长公共后缀的长度。 具体来说,next数组的定义如下: 1. next = -1,表示模式串的第一个字符没有前缀和后缀。 2. 对于模式串中的每个位置i(1 <= i < 模式串长度),next[i]表示模式串前缀子串[0, i-1]中最长的既是前缀又是后缀的子串的长度。 通过构建next数组,可以在匹配过程中根据已匹配的前缀信息来决定下一步的移动位置,从而避免不必要的比较。 下面是构建next数组的步骤: 1. 初始化next = -1,j = 0,i = 1。 2. 当i < 模式串长度时,执行以下步骤: - 如果模式串的第i个字符与模式串的第j个字符相等,则令next[i] = j,i++,j++。 - 如果模式串的第i个字符与模式串的第j个字符不相等: - 如果j = 0,则令next[i] = 0,i++。 - 如果j != 0,则令j = next[j],回溯到上一个最长公共前缀和最长公共后缀的长度,继续比较。 构建完next数组后,可以根据next数组来进行字符串匹配,具体步骤如下: 1. 初始化文本串的指针i = 0,模式串的指针j = 0。 2. 当i < 文本串长度时,执行以下步骤: - 如果文本串的第i个字符与模式串的第j个字符相等,则i++,j++。 - 如果j = 模式串长度,则表示匹配成功,返回匹配位置。 - 如果文本串的第i个字符与模式串的第j个字符不相等: - 如果j = 0,则i++。 - 如果j != 0,则令j = next[j],回溯到上一个最长公共前缀和最长公共后缀的长度,继续比较。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值