题意:一共c组数据,每组数据有两个字符串,求第一个字符串在第二个字符串中出现的次数。
思路:kmp模板题
kmp:相较于n*m的字符串匹配算法每次匹配失败后是一位一位移动模式串,kmp就相当于是一次多位的移动模式串par。
每次匹配失败移动位数的依据则是依靠next数组存储。
next[i]=max(0<=k<i,par[1 ... k]==par[i-k+1 ... i])若k=0则字符串为空。
next的求法:当我们已知next[1]到next[i-1]时,要求next[i]。
设next[0]=-1。根据next的定义,则par[i]=par[next[i]],而par[1 ... next[j]]==par[i-next[j] ... i-1](1<=j<i)。
那么如何快速求j?每次匹配par[1 ... i-1]和par[1 ... i]。j=next[i-1]。若par[j+1]!=par[i]则j=next[j]。
每次循环就相当于找一个j,有par[1 ... j]==par[i-j ... i-1],如此往复,直到有解或者j=-1。那么next[i]=j+1。
next的用法:与求法一致,只不过现在是用模式串par和原串ori进行匹配。设par下标为i,ori下标为j。
- 循环直到par[i+1]!=ori[j+1]。
- 若i+1>par的长度则匹配成功sum++,i=next[i],否则匹配失败。
- 若j+1>ori的长度则退出循环。
- 循环i=next[i]直到par[i+1]==ori[j+1]或者i==-1。
- 若i==-1则说明ori[j]已不存在解。则++i,++j。
- 若j<ori的长度则返回步骤1。
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
#define NUM 11000
string ori,par,s;
int c;
int ans;
int _next[NUM];
int getnext(int x)//求next数组
{
int i;
i=_next[x];
while(i!=-1&&par[i+1]!=par[x+1])
i=_next[i];
return i+1;
}
int solve()//计数
{
int i=0;
int j=0;
int cou=0;
while(j<ori.size())
{
while(i+1<par.size()&&j+1<ori.size()&&ori[j+1]==par[i+1])
{
++i;
++j;
}
if(i+1==par.size())++cou;
if(j+1==ori.size())break;
while(i+1==par.size()||(i!=-1&&par[i+1]!=ori[j+1]))
i=_next[i];
if(i==-1)
{
++j;
++i;
}
}
return cou;
}
int main()
{
scanf("%d",&c);
_next[0]=-1;
_next[1]=0;
while(c--)
{
par=ori=" ";
cin>>s;
par+=s;
cin>>s;
ori+=s;
for(int i=2;i<par.size();++i)
_next[i]=getnext(i-1);
ans=solve();
printf("%d\n",ans);
}
}