// 此博文为迁移而来,写于2015年5月24日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w1iw.html
1、前言
好吧我得承认这东西应该是早就要会了的。。。虽然感觉上用的不多,但是当我开始接触AC自动机的时候,发现这是一个很必要的知识点,所以今天来讲一讲。
然而有一个问题了——为什么我一直没有搞懂就是因为许多许多次我看网上的一些文章就发现总是弄得很复杂,所以我推荐大家直接看代码,更容易弄懂。反正我就这么明白了。在AC自动机明白之后,将会有更详细地阐述。
2、fail数组
KMP算法,是在O(n2)普通字符串匹配基础上的一个改进版本,KMP算法的关键是利用每次匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。若读入s1,s2两个字符串(s1.len最最核心的部分在于读入s1串的时候,利用一个fail[ ](也可以命名为next[ ])数组,其包含了模式串的局部匹配信息。这就是很多地方讲的很多很复杂的地方。所以说如果你想清楚透彻地搞懂这个数组存在的意义,还是推荐你去看其他人的博文,很详细。用我简单的话来讲,fail[i]表示s1的第i位起的后面部分子串与s1本身整个串的最长前缀个数。举几个生动的例子:
1、“hellohehhelaa“ fail[ ]={0,0,0,0,0,1,2,1,1,2,3,0,0}
2、“helloworldhowareyou“ fail[ ]={0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0}
3、“bowbowbowbowb“ fail[ ]={0,0,0,1,2,3,4,5,6,7,8,9,10}
4、“refrigerator“ fail[ ]={0,0,0,1,0,0,0,1,0,0,0,1}
这个步骤我们在进行匹配之前将其处理好。
3、利用fail匹配
得到了fail数组,接着就要匹配了。我们的fail数组作用就在于每一次匹配失败之后不要又分别从s1,s2开头匹配,那样就是传说中的O(n2)算法了。根据之前得到的信息,我们可以跳过一些绝对匹配不上的字符以减低复杂度。
4、代码
----------------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#define MAXN 205
int fail[MAXN],temp,la,lb;
char s1[MAXN],s2[MAXN];
int main()
{
freopen("KMP.in","r",stdin);
freopen("KMP.out","w",stdout);
scanf("%s",s1+1); la=strlen(s1+1);
scanf("%s",s2+1); lb=strlen(s2+1);
fail[0]=-1;
for (int i=1;i<=la;i++)
{
temp=fail[i-1];
while (temp!=-1 && s1[temp+1]!=s1[i]) temp=fail[temp];
fail[i]=temp+1;
}
temp=0;
for (int i=1;i<=lb;i++)
{
while (temp!=-1 && s1[temp+1]!=s2[i]) temp=fail[temp];
temp++;
if (temp>=la) { printf("YES\n"); return 0; }
}
printf("NO\n");
return 0;
}
----------------------------------------------------------------------------------------------------