本文只为自己记住kmp算法,在csdn留个笔记方便以后自己找得到,只留核心点,不做过多解释,需要学习kmp算法的看其他博客哈。
next数组的含义:
next[6]=3 表示pattern[0:6](闭区间,包含头尾)中,后缀串等于前缀串的最长后缀串长度为3.下面说成最长相等前缀串或者最长相等后缀串为3。
比如
abcdabc。最长相等后缀串是abc,长度为3。后缀abc长度为3,他等于前缀。他已经是最长的了,更长的后缀dabc,他不等于前缀abcd。
kmp算法的第一步骤就是求模式串的next数组。
其中规定next[0] = 0。因为前缀后缀不能是同一字符串。
然后用迭代的方法算出next[1]到next[pattern.size()-1]。
记j为本次要算的next值。即本次要计算next[j],
记k为上次算出的next值。即k = next[j-1]。k一方面表示,比待计算的字串长度短1的字串,他的最长相等后缀串长度为k。同时,他也表示比待计算的字串长度短1的字串,他的最长相等前缀串的下一个字符的下标。如图

计算方法如下:
情况1:如果 pattern[j] = pattern[k]
那么next[j] = k+1,同时,把k更新为 k = next[j]。同时,把j更新为j = j+1。
如果 pattern[j] != pattern[k],那就得找到比上图蓝色部分更短的字串。他的长度最多是next[ k-1 ] (如果这个更短的字串存在,他肯定以上图中左边的蓝色部分的最后一个字符结尾)。做法就是把k更新为next[k-1]。然后再次判断pattern[j] 是否 = pattern [k],如果相等,说明这个更小的字串是合适的,那么next[j] = k+1,同时,把k更新为 k = next[j]。同时,把j更新为j = j+1。(情况1)。如果不相等,说明这个比蓝色还短的字串还不够短,就继续 k = next[k-1]。这个步骤持续到 k0时。因为当k0时,next[k-1]=next[-1]是不合法的。当k =0时,他们还不相等,那么就让next[i] = 0。表示没有相同前后缀。
用代码表示就是
vector<int> next(pattern.size(),0);
next[0]=0;
int k=next[0];
for(int i=1;i<pattern.size();){
if(pattern[i]==pattern[k]) {
next[i]=k+1;
k=next[i];
i++;
}
else if(k==0) next[i++]=0;
else k=next[k-1];
}
算出 next数组后,剩下的就是kmp算法了。
用i指针指向主串,j指针指向模式串。
kmp算法的i指针只会向右移动。
比较main[i] 和 pattern[j]。如果他们相等。那他们都向前移动一格。
如果不相等,则让j回溯到next[j-1]。这个步骤的图如下

这时再比较i和j。如果还不相等。最让j再回溯到next[j-1]。
直到main[i] =pattern[j]。或者 j ==0。
如果j == 0,那就让i向前移动,表示i指针当前位置无法与模式串匹配。代码如下:
for(int i=0,j=0;i<main.length();){
if(main[i]==pattern[j]){
i++;j++;
if(j==pattern.size()) return i-j;
}
else if(j>0) j=next[j-1];
else i++;
}
总体代码如下:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int kmp(string main,string pattern){
if(pattern.size()>main.size()){
return -1;
}
vector<int> next(pattern.size(),0);
next[0]=0;
int k=next[0];
for(int i=1;i<pattern.size();){
if(pattern[i]==pattern[k]) {
next[i]=k+1;
k=next[i];
i++;
}
else if(k==0) next[i++]=0;
else k=next[k-1];
}
cout <<endl;
for(int i=0;i<next.size();i++){
cout << next[i]<<" ";
}
cout<<endl;
for(int i=0,j=0;i<main.length();){
if(main[i]==pattern[j]){
i++;j++;
if(j==pattern.size()) return i-j;
}
else if(j>0) j=next[j-1];
else i++;
}
return -1;
}
int main(){
string mainString = "adbcafabcadcabfgabcabfgabcabcabfabcdabdadva";
string pattern = "abcabfgabcabcabf";
cout << kmp(mainString,pattern)<<endl;
}
696

被折叠的 条评论
为什么被折叠?



