KMP算法的用途
KMP算法常常用于在一个目标字符串S中找到模板串T的位置,我们平常碰到这种匹配问题,可能最先想到的就是暴力匹配。这种方法对于数据规模不大的时候是较为有效的,但是在数据规模比较大的情况下,往往会造成TLE。那么该怎么办呢?这时候我们就可以请出我们的KMP算法了。KMP算法在牺牲了一些空间的条件下,却大大的提升了我们的时间效率,经典的“以空间换时间”(优势在我)。
KMP算法的理解
事实上KMP算法的代码量并没有多大,但是这个算法极其诡异,令人匪夷所思。
先贴上代码
ll n,k,t;
char p[maxn];
char s[maxn];
int lo[maxn];
void solve()
{
cin>>n>>p+1>>k>>s+1;
for(int i=2,j=0;i<=n;i++)
{
while(j&&p[i]!=p[j+1])
{
j=lo[j];
}
if(p[i]==p[j+1])
{
j++;
}
lo[i]=j;
}
for(int i=1,j=0;i<=k;i++)
{
while(j&&s[i]!=p[j+1])
{
j=lo[j];
}
if(s[i]==p[j+1])
{
j++;
}
if(j==n)
{
cout<<i-n+1<<endl;
j=lo[i];
}
}
}
事实上,这个算法里面最为重要的是搞清楚lo数组存的是什么,将它搞懂,我们对于这个算法的理解将变得较为容易许多。
事实上,这个lo数组存的是模板串里面第i个字符的(相等的最大前缀与最大后缀)。例子如下
假设我们的目标串为S,模板串为P
S=“abababc"
P=“abab”
其中lo[1]=0,由于1前面没有字符,所以事实上lo数组的确定是从2开始的。
lo[2]=0,其中我们前缀和后缀的长度不得大于当前位置i,这里的前缀为a,后缀为b,不相等。所以为0.
lo[3]=1,其中前缀={“a”,“ab”"},后缀={“a”,“ba”},这里相等的最大长度为1.
lo[4]=2,其中前缀={“a”,“ab”,“aba”},后缀={“b”,“ab”,“bab”},这里相等的最大长度为2。
所以看到了吧,这个lo数组存的就是这么一个东西。
好,现在我们就来探究这个是什么作用,这个也是该算法最主要的
j=lo[j];
令S=“babaca”,T=“babab”;
当i=5时,S[i]=‘c’,T[i]=‘b’,这里产生不匹配。
由于我们此时匹配的字符串长度为4,所以我们此时要进行回溯,将模板串指针j的位置从4调整到2,因为在前四个字符中,最大前缀与相等的最大后缀为2。
如此调整的理由如下:
虽然在5处未匹配,但是这也说明了目标串在该段之前的4个字符是与我们的模板串匹配的,而且前四个字符串的最大相等前缀和后缀为2,所以我们可以将j移动到2的位置,从此处开始进行匹配。这样我们就略过了前面的检验,节省了时间。
由于是刚开始写博客,所以写的很烂,如果有想更好了解该算法的,可以参考这篇
KMP 算法详解