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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值