hihocode1015-KMP 算法探讨
题目如下
输入
第一行一个整数N,表示测试数据组数。接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。其中N<=20
输出
对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。
样例输入
5
HA
HAHAHA
WQN
WQN
ADA
ADADADA
BABABB
BABABABABABABABABB
DAD
ADDAADAADDAAADAAD
样例输出
3
1
3
1
0
算法解释
KMP引出
源 串:babababababababb
模式串:bababb
请问原串中有几个这样的模式串?
一目了然,只有一个,但如果用计算机程序去判别的话,用一个一个去匹配的方法十分浪费时间。
源 串:babab|abababababb
模式串:babab|b
发现在第6 个字符处有了分歧和误差,此时有两种情况
第一,源串和模式串的新的对齐点在此字符之后(包括此字符);
第二,源串的这字符正好和模式串的某个字符匹配对齐;
在这里我们讨论第二种情况
源串: babab|abababababb
模式串(对齐点=1):babab|b
模式串(对齐点=3): bab|abb
我们会发现这样一个问题,假设我们以i表示两个串发生分歧的第一个地方,用j来表示到达新的对齐点模式串移动的字符个数,我们会发现这样一个规律,对于模式串而言有[1,j]和[j,2j-1]是相等的,我们回发现在[1,2j-1]段,模式串的字符是对称的,而i为第一个发生分歧的点,则有2j-1=i-1,因此这个等式可以换成这样[1,i-j]=[j,i-1],这有这样的情况下我们才能进入到分歧点i的匹配环节,因此我们需要对模式串的每一个分歧位置位置寻找这样的一个j值。这些j值就可以组成一个新的数组,这个数组就是NEXT数组。这个定义还不是很好,还需要更加精确的定义。
NEXT数组
NEXT[0]=-1
NEXT[i]=max{0<=k
NEXT数组的应用
知道了NEXT数组,我们需要知道如何使用NEXT数组
针对我们所距离的模式串有
模式串:bababb
NEXT: 001231
于是针对源串ori[p]和模式串par[q],我们有
源串(p=5): babab|abababababb
模式串(q=5):babab|b
此时匹配到第五个字符时就无法向下匹配,因为ori[p+1]!=par[q+1],可以令q=NEXT[q],则有
源串(p=5): babab|ab|ababababb
模式串(q=3): bab|ab|b
此时发现ori[p+1]==par[q+1],则可以往下匹配,于是p++,q++,当p=7,q=5时,又出现了不匹配。
源串(p=7): bababab|ababababb
模式串(q=5): babab|b
此时可另q=NEXT[q],依次类推下去,当出现q=-1是则表明模式串越过了匹配点,此时需要跳过这个点,p++,q++,然后接着经行判断
NEXT数组求解
知道的NEXT数组的用法,我们需要知道NEXT如何求来
如果我们已知NEXT[1..4],如何知道NEXT[5]?
我们可以令par.substring(1..5)为源串,par.substring(1..4)为模式串,即
源串(p=4): baba|b
模式串(q=4):baba
模式串第五个字符是空字符,故肯定无法匹配,可以令q=NEXT[q],则
源串(p=4): baba|b
模式串(q=2): ba|ba
p++,q++时,我们发现ori[p]==par[q],可以匹配上,而此时NEXT[p]=q
即NEXT[5]=3。
代码样例
#include <stdio.h>
#include <string.h>
int main() {
int n;
char p[10010],s[1000010];//p为模式串,s为源串
scanf("%d",&n);
while(n--)
{
scanf("%s%s",p,s);
int next[10010]={};
int i=0,j=-1;
next[0]=-1;
int lenp=strlen(p);
int lens=strlen(s);
while(i<lenp) //next数组的生成
{
if(j==-1||p[i]==p[j])
next[++i]=++j;
else
j=next[j];
}
int ans=0;
i=j=0;
while(i<lens)
{
if(j==-1||s[i]==p[j])
{
++i;++j;
}
else
j=next[j];
if(j==lenp) ans++;
}
printf("%d\n",ans );
}
return 0;
}