KMP算法的next数组实现

KMP算法

for leetcode 实现strStr()

前不久打虎符CTF的qual时候做过一道redemption_code 的逆向题。就是要逆向一个kmp算法,我那时候甚至没听说过这个算法,不过把它当黑盒也猜出了关键函数是在找子串。赛后了解了一下kmp算法的原理,但没有自己写一遍。然后转头就碰到了LeetCode的每日一题要写kmp。

行吧,那么自己写一下。
kmp的关键就是如何得到next数组,LeetCode里面的推导结合代码看的话会比较清楚。我在代码里加了注释,配合LeetCode的官方题解,希望能尽量解释清楚kmp算法。

首先,对于长度为 m 的字符串 s,其前缀函数 π ( i ) ( 0 ≤ i < m ) \pi(i)(0 \leq i < m) π(i)(0i<m) 表示 s 的子串 s [ 0 : i ] s[0:i] s[0:i] 的最长的相等的真前缀与真后缀的长度。特别地,如果不存在符合条件的前后缀,那么 π ( i ) = 0 \pi(i) = 0 π(i)=0。其中真前缀与真后缀的定义为不等于自身的的前缀与后缀。官方的例子如下:

next数组的例子

下面就是我们代码所基于的推导,看懂这部分其实就知道怎么实现kmp算法了。其中 π ( i ) \pi(i) π(i) 数组就是我们要求的next数组,我在代码注释中就不在加以区分了。
在这里插入图片描述

其实写完next数组的获得部分以后觉得其实好像就是一个初始条件为 π ( 0 ) = 0 π(0) = 0 π(0)=0的dp的思想,next数组本质就是一个dp数组。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;

class Solution {
public:
    int strStr(string haystack, string needle) {
        if (needle=="") return 0;
        if(needle.length()>haystack.length()) return -1;
        /* 获得next数组*/
        vector<int> next(needle.length(),0);
        int i=1;
        int j = 0; // next[0]一定等于0, 这里其实写成j=next[0]更好
        while(i<needle.length()){
            if(needle[j]==needle[i]){ // 当s[i]=s[π(i−1)]时,π(i)=π(i−1)+1 即推导的第二个式子
                next[i] = j+1;
                i++;j++;
            }else if(j){  // needle[j]!=needle[i] 且j不为零  
                j = next[j-1];//进行回退,j-1是上一个匹配成功的,所以j=π(j-1) 其实就是上面推导中红框的部分
            }else{       // needle[j]!=needle[i] 回退到头了  只能取0
                next[i++] = 0;
            }
        }

        /*用next数字进行子串的匹配*/
        j=0;i=0;
        for(i=0;i<haystack.length();i++){
            while(j && haystack[i]!=needle[j]){
                j = next[j-1]; // j匹配失败了而j-1是上一个匹配成功的,所以j=next[j-1]进行回退
            }
            if(haystack[i]==needle[j]) j++;
            if(j==needle.length()) return i-needle.length()+1;
        }
        return -1;
    }
};

int main(){
    Solution a;
    cout << a.strStr("mississippi","issip") <<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法中的next数组是用来记录模式串中每个位置之前的最长相同前缀后缀的长度。具体来说,next数组的每个元素next\[i\]表示当模式串的第i+1个字符与文本串不匹配时,模式串应该跳过的位置。\[2\]在KMP算法实现中,通过使用next数组,可以在匹配过程中避免不必要的回溯,提高匹配效率。 next数组的求法有多种方法,其中一种常用的方法是通过动态规划的思想来计算。具体步骤如下: 1. 初始化next数组,将next\[0\]置为-1,next\[1\]置为0。 2. 从模式串的第2个字符开始,依次计算每个位置的next值。 3. 对于位置i,如果模式串的第i个字符与前缀的下一个字符相等,则next\[i\]等于前缀的长度加1。 4. 如果模式串的第i个字符与前缀的下一个字符不相等,则需要根据已知的next值来更新next\[i\]。 - 如果next\[j\]等于-1,或者模式串的第i个字符与前缀的下一个字符相等,则next\[i\]等于j。 - 如果next\[j\]不等于-1,且模式串的第i个字符与前缀的下一个字符不相等,则需要继续向前回溯,即将j更新为next\[j\],然后再进行比较。 - 重复上述步骤,直到找到一个满足条件的j或者回溯到模式串的起始位置。 通过以上步骤,可以得到完整的next数组,用于KMP算法的匹配过程中的跳转操作。\[2\]在KMP算法的代码实现中,可以根据next数组的值来决定模式串的后移位置,从而提高匹配效率。\[3\] #### 引用[.reference_title] - *1* *2* [KMP算法&next数组详解](https://blog.csdn.net/ooblack/article/details/109329361)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [KMP 算法中的 next 数组](https://blog.csdn.net/m0_52423355/article/details/123807325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值