浅谈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( );
}