介绍
- KMP 是一个解决模式串在文本串是否出现过,如果出现过,最早出现的位置的经典算法,其实现过程非常精妙
- Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP 算法”,常用于在一个文本串 S 内查找一个模式串 P 的出现位置,这个算法由Donald Knuth、Vaughan Pratt、James H. Morris 三人于 1977 年联合发表,故取这 3 人的姓氏命名此算法。
- KMP 方法算法就利用之前判断过的信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过 next 数组找到,前面匹配过的位置,省去了大量的计算时间。时间复杂度为O(n+m)
当你学会这个算法你会觉得是如此的unbelievable!!!
实现
KMP算法的主要思想是利用模式串本身的重复信息,构造一个next表:第i位标识从第1个到第i个字符的最长公共前后缀,在匹配失败时,通这个表确定下一次匹配的位置。我们已经知道,前后某部分字串相同,所以当模式串第i位字符串匹配不上时,我们取第i-1位的最长公共前后缀的长度,将前缀移到当前位置,大大缩短了查找时间。
如何去求next呢?
就相当于模式串自己和自己匹配
当前一位匹配成功,就在前面的基础上长度加一,即next[i-1]加1,
如果不是,就回溯,找上一位最长公共前后缀的下一位
KMP代码:
string S,P;//字符串使用前要在前面加一个“ ”例如:P=" "+P;
int Next[N];
void getnext(string P){
int i,j;
for(Next[1]=j=0,i=2;P[i];i++){
while(j&&P[i]!=P[j+1]) j=Next[j];
if(P[i]==P[j+1]) j++;
Next[i]=j;
}
}
void KMP(string S,string P){
int i,j;
for(int j=0,i=1;S[i];i++){
while(j&&S[i]!=P[j+1]) j=Next[j];
if(S[i]==P[j+1]) j++;
if(!P[j+1]){
//code......
j=Next[i];//重复的匹配
//j=0;//不重复匹配
}
}
}
例题
AC代码:照抄模板
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5;
string S,P;
int Next[N];
void getnext(string P){
int i,j;
for(Next[1]=j=0,i=2;P[i];i++){
while(j&&P[i]!=P[j+1]) j=Next[j];
if(P[i]==P[j+1]) j++;
Next[i]=j;
}
}
void KMP(string S,string P){
int i,j;
for(int j=0,i=1;S[i];i++){
while(j&&S[i]!=P[j+1]) j=Next[j];
if(S[i]==P[j+1]) j++;
if(!P[j+1]){
cout<<i-j+1<<endl;
j=Next[j];
}
}
}
int main(){
cin>>S>>P;
S=" "+S;
P=" "+P;
getnext(P);
KMP(S,P);
for(int i=1;i<=P.size()-1;i++){
cout<<Next[i]<<" ";
}
return 0;
}