题意:求S2串从i开始到结尾的子串在S1串中出现的次数,用次数乘子串长度,最后把所有的相加
思路:利用KMP的方法,在KMP时,我们可以记录从0开始长度为i的子串出现的次数,这就需要我们先把两字符串倒置。但是这个记录是不全的,因为我们不是从头开始进行比较。我们想想KMP算法中,每一次需要跳转的时候,是从nextval[j]开始继续比较,假如说能够匹配,那我们就继续比了,也就是前面的nextval[nextval[j]]等被我们忽略掉了,没有将他们出现的次数增加。再想一想,长度为j的子串出现的次数,应该就是我们忽略掉的长度为nextval[j]的子串出现的次数。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s1[1000001],s2[1000001];
long long nextval[1000001];
long long num[1000001];
long long mod=1e9+7;
long long len1,len2;
void get_nextval()
{
long long i=0;
nextval[0]=-1;
long long j=-1;
while(i<len2)
{
if(j==-1||s2[i]==s2[j])
{
i++;
j++;
nextval[i]=j;
}
else
j=nextval[j];
}
}
/*
不能使用这种写法,因为这种跳回去的时候可能跳过一些子串
void get_nextval()
{
long long i=0;
nextval[0]=-1;
long long j=-1;
while(i<len2)
{
if(j==-1||s2[i]==s2[j])
{
i++;
j++;
if(s2[i]!s2[j])
nextval[i]=j;
else
nextval[i]=naxtval[j];
}
else
j=nextval[j];
}
}
*/
void KMP()
{
long long i=0;
long long j=0;
memset(num,0,sizeof(num));
while(i<len1)
{
if(j==-1||s1[i]==s2[j])
{
i++;
j++;
}
else
j=nextval[j];
num[j]++; //num[j]记录长度为j的子串匹配次数
if(j==len2)
j=nextval[j];
/*这里的num[j]++记录不全。
当s2[0..j]可以匹配时,未记录s2[0..nextval[j]]等子串
*/
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s1);
scanf("%s",s2);
len1=strlen(s1);
len2=strlen(s2);
reverse(s1, s1 + len1);
reverse(s2, s2 + len2);
get_nextval();
KMP();
long long sum=0;
for(long long i=len2;i>0;i--)
{
num[nextval[i]]+=num[i];
//长度为i的出现了多少次,长度为nextval[i]就少记录了多少次
sum=(sum+num[i]*i)%mod;
}
printf("%lld\n",sum);
}
return 0;
}