参考链接:KMP 算法(1):如何理解 KMP
//
// kmp.cpp
// algorithm
//
// Created by soybeanmilk on 2017/6/11.
// Copyright © 2017年 soybeanmilk. All rights reserved.
//
#include <iostream>
#include <string>
using namespace std;
/*P为模式串,下标从0开始*/
void GetNext(string P,int next[]){
unsigned long p_len = P.size();
int i=0;
int j=-1;
next[0] = -1;
while(i<p_len){
if(j==-1 || P[i] == P[j]){
i++;
j++;
next[i] = j;
//如果P[i]==P[j],那么i+1为止的最大的前缀等于j+1
//加1的原因:这个next数组是这样用的:当模式串中第n位与待匹配串中第m位不同时,
//用next数组来指示下次模式串中哪一位(因为这个是基于最长相等前后缀求的,所以直接跳过第n位前面的与第m位前面的的元素)与待匹配串中m位相比较,这也就说明了第n位前面的与第m位前面的已经相等了,
//通过next数组使模式串滑动是大的距离去做比较。
//也正是基于上面这一点,才会有下面的改进的kmp。
}
else
j = next[j];
//到这里时,P[i-1]==P[j-1],由因为是从前往后推的next[i],所以,
//如果P[i]!=P[j],那么要往前找,肯定是先找与P[i-1]相等的元素的下标,也就是找与P[j-1]相等的元素的下标。
//而,next[j]其实就是0~j-1时的最长相等前后缀的长度,故P[0~next[j]]等于P[(j-(next[j]+1))~(j-1)],所以,
//P[next[j]]就等于P[j-1],所以往前找时,令j=next[j],依次往前推。
}
}
/* P为模式串,下标从0开始 */
void GetNextval(string P,int nextval[]){
unsigned long p_len = P.size();
int i=0;
int j=-1;
nextval[0] = -1;
while(i<p_len){
if(j==-1 || P[i]==P[j]){
i++;
j++;
if(P[i]!=P[j])
nextval[i]=j;
else
nextval[i]=nextval[j];
//上面的函数的分析中可以知道:如果模式串中第i位与待匹配串中第m位不同,而这里P[i]与P[j]相同时,再比较P[j]与待匹配串中第m位是否相等是多余的操作。所以往前找不同的元素。
//又由于这里是从前往后推nextval[i]的,所以,P[nextval[i]]与P[nextval[j]]肯定是不同的,所以只需要往前找一次就行了,而不需要像j=nextval[j]一样有可能往前推到-1处。
}
else
j=nextval[j];
}
}
/*在S中找到P第一次出现的位置*/
int KMP(string S,string P,int next[]){
//GetNext(P,next);
GetNextval(P,next);
int i=0;
int j=0;
int s_len = (int)S.size();
int p_len = (int)P.size();
while(i<s_len && j<p_len){
if(j==-1 || S[i] == P[j]){
i++;
j++;
}
else
j=next[j];
}
if(j==p_len)
return i-j;
return -1;
}
int main(){
int next[100]={0};
cout<<KMP("bbc abcdab abcdabcdabde", "abcdabd", next) << endl;
return 0;
}