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)(0≤i<m) 表示 s 的子串 s [ 0 : i ] s[0:i] s[0:i] 的最长的相等的真前缀与真后缀的长度。特别地,如果不存在符合条件的前后缀,那么 π ( i ) = 0 \pi(i) = 0 π(i)=0。其中真前缀与真后缀的定义为不等于自身的的前缀与后缀。官方的例子如下:
下面就是我们代码所基于的推导,看懂这部分其实就知道怎么实现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;
}