浅谈KMP
浅谈KMP
简介
KMP是由三位大神联合发表的
分别是
D.E.Knuth
J.H.Morris
V.R.Pratt
取其首字母,叫KMP算法
这个算法作用是解决字符串匹配问题
时间接近复杂度O(n+m)
暴力解决
算法思路:
从每一位开头,向后匹配
如果匹配完了返回找到
如果每一位都试过了就没找到
O(n*m)
#include<bits/stdc++.h>
using namespace std;
string a,b;
int n,m;
int main( ){
cin>>a>>b;
n=a.size( );m=b.size( );
int i,j;
for(i=0;i<n;i++){
j=0;
while(j<m&&a[i+j]==b[j])j++;
if(j==m)goto OK;
}
goto GG;
OK:
puts("Yes");
return 0;
GG:
puts("No");
return 0;
}
暴力算法不足处
对于字符串aaaaaaaaaaab与aaaaab
我们发现每次匹配完
都是从下一位开始
KMP算法思路
但我们人为匹配是怎么样的?
对于aaac与aac
我们发现aac无法与aaa匹配
那么就将aac第3位与aaac第的第4位匹配
就找到了
KMP算法实现
我们发现如果
abaaabc
abaaaba
的在c位无法匹配
下次不是将aaaaac从第二位重新比较
而是将倒数第3位的a与最后一位a重新比较
为什么呢?
我们发现倒数第二位之前的字符串后缀和前缀最大就是ab
于是发现有重复的部分
也就是说可以跳过ab和中间的进行匹配
KMP算法核心之一,next数组
那我们的任务就是求出next数组(即最长相等前缀后缀)
next[0]=-1;
next[1]=0;
while(j<m){
if(k==-1||b[j]==b[k]){
++j;++k;
next[j]=k;
}else k=next[k];
}
详细有很多人看不懂k=next[k]吧
这里有点绕,但是并不难
因为对于字符串
AAABAAAA
| |
我们发现B与A不匹配
呢么就要回退
回退到哪里呢?next[k]即指针所指的地方
再次与当前位置比较
KMP算法
理解了前面那个,就是对思路的实现了
#include<bits/stdc++.h>
using namespace std;
void work( ){
int next[10000];
string a,b;
int j=0,k=-1,i,n,m;
cin>>a;
if(a=="#")exit(0);
cin>>b;
n=a.size( );
m=b.size( );
next[0]=-1;
next[1]=0;
while(j<m){
if(k==-1||b[j]==b[k]){
++j;++k;
next[j]=k;
}else k=next[k];
}
int ans=0;
i=0;j=0;
while(i<n){
if(j==-1||a[i]==b[j]){
if(j==m-1)goto OK;
++i;
++j;
}else j=next[j];
}
goto GG;
OK:
puts("Yes");
return;
GG:
puts("No");
return;
}
int main( ){
while(1)work( );
}
例题
简单的KMP应用
#include<bits/stdc++.h>
using namespace std;
void work( ){
int next[10000];
string a,b;
int j=0,k=-1,i,n,m;
cin>>a;
if(a=="#")exit(0);
cin>>b;
n=a.size( );
m=b.size( );
next[0]=-1;
next[1]=0;
while(j<m){
if(k==-1||b[j]==b[k]){
++j;++k;
next[j]=k;
}else k=next[k];
}
int ans=0;
i=0;j=0;
while(i<n){
if(j==-1||a[i]==b[j]){
if(j==m-1)ans++;
++i;
++j;
}else j=next[j];
}
cout<<ans<<endl;
}
int main( ){
work( );
}

浙公网安备 33010602011771号