AC自动机铺垫章节,必备前置技能
KMP算法,俗称看毛片算法,是一种线性复杂度的高效字符串匹配算法,虽然工程代码运用很少(因为现实生活不会有精心构造的“良心数据”),但是在算法竞赛中非常实用高效。
按照惯例给出原理详解代码(写的真心很好,巨详细,超耐心,自带配图):神犇的博客
很多的详解都是按这篇博客仿出来的(有的甚至直接复制),侧面说明这篇博客是非常良心的了,只是文字量略大。。(都说了非常良心了),这篇博客里的程序能求出寻找的目标字符串在文本里第一次出现的位置,而博主的这篇博客给出的程序能求出目标串出现的所有位置(其实只是做了一个很小很简单的的改动2333)
模板题链接:洛谷P3375
博主的最近几篇博客都很水,其实是为AC自动机做铺垫(顺便水几篇博客维持一下我的持之以恒勋章),AC自动机可能会认真写一篇博客。。。
KMP的代码量其实非常小,主要是原理理解上的问题,不过既然有神犇的博客做挡箭牌,博主这里自然就偷一波懒。
先用个递推求出next数组的值,next[i]表示前i个字符构成的字符串中相同前后缀的长度(多次阅读以便理解),至于这个next数组有什么用,为什么使用这个next,原理是什么,请各位老爷参考前方的大佬博客:
void n()
{
next[0]=-1;
int k=-1,j=0;//强势赋初值
while(j<len2)
{
if(k==-1||ch2[j]==ch2[k])
{//如果前缀与后缀可以匹配,记录next,进入下一步
j++;
k++;
next[j]=k;
}
else
{//如果匹配不上,k就往回跳
k=next[k];
}
}
}
这个过程跟kmp本身其实是一个原理,利用已知的next求剩余的,其中失配时k的跳转跟kmp是一样的,深入理解了以后其实是很简单的。
然后就是利用next数组进行匹配了,只需遍历一遍,复杂度是线性的。
void kmp()
{
int i=0,j=0;
while(i<len1)
{
if(ch1[i]==ch2[j]||j==-1)
{//如果匹配成功或者j是第一个字符的next状态,继续匹配下一个
i++;
j++;
if(j==len2)
{//如果目标串匹配完毕,输出一个位置,重置j
printf("%d\n",i-j+1);
j=next[j];
}
}
else
{//如果失配了,也重置
j=next[j];
}
}
}
重置j的过程是最难理解的,请读者多多膜拜大佬的博客,多多打代码。
最后附上模板题的AC代码:
#include<bits/stdc++.h>
using namespace std;
char ch1[1000005],ch2[1000005];
int len1,len2;
int next[1000005];
void n()
{
next[0]=-1;
int k=-1,j=0;
while(j<len2)
{
if(k==-1||ch2[j]==ch2[k])
{
j++;
k++;
next[j]=k;
}
else
{
k=next[k];
}
}
}
void kmp()
{
int i=0,j=0;
while(i<len1)
{
if(ch1[i]==ch2[j]||j==-1)
{
i++;
j++;
if(j==len2)
{
printf("%d\n",i-j+1);
j=next[j];
}
}
else
{
j=next[j];
}
}
}
int main()
{
scanf("%s%s",ch1,ch2);
len1=strlen(ch1);
len2=strlen(ch2);
n();
kmp();
for(int i=1;i<=len2;i++)
{
printf("%d ",next[i]);
}
return 0;
}