“KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。”——百度百科
1.干啥的
由于本算法是由发明他的三位巨佬命名的,所以乍一看似乎无法明白这玩意是干啥用的。简单来说,这个算法的最大用处就是查找关键字。即在字符串中寻找子串的位置。越听越像find
2.正文开始
首先前方出现了两个字符串
我们要做的,就是找出短字符串在长字符串中出现的位置(主串中找子串)。其实这个问题本身很简单,只要是学过字符串都会想到可以从左到右一位一位匹配
如果相同,则继续匹配,如果不同的话:
把i变成1,j变回0,重新搜。
那么我们就得到了朴素版算法:
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s1,s2;
int i=0,j=0;
cin>>s1>>s2;
while (i<s1.size()&&j<s2.size())
{
if (s1[i]==s2[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if (j==s2.size())
{
cout<<i-j;
}
else
{
cout<<-1;
}
}
是个好算法但是不够快
如果是手模的话,我们绝对不会直接把i赋为1,而是会这样找:
人会直接定位到下一个A,而不是一位一位挪下去,所以出现了!野生的KMP算法!
从上面我们可以理解到,KMP的核心就是我们要在匹配失败时找到两个指针要去的地方
例如上图,不必直接回到i=1,j=0,而是i=5,j =3
我们不难看出,s1[i~x]==s1[j-x~j-1]
稍微理解一下,如果匹配失败,那么j完全可以直接跳到x+1,而不是0
综上所述,只要把每个点对应的x都存起来,跳的时候直接跳到next[j]+1就可以省不少时间
void nextt()
{
next[0]=-1;//防止死循环
int j=0,x=-1;
while (j<s2.size()-1)
{
if (x==-1||s2[j]==s2[x]) {
next[++j]=++x;
} else {
k=next[x];
}
}
}
仔细推一下就可以发现,当s2[x]==s2[j]时,next[j+1]=next[j]+1
后面就好搞了