传送门:点击打开链接
题意就是求一个串T的所有后缀在串S中出现的次数。将S,T都翻转之后就是求T的所有前缀在S中出现的次数。
拓展KMP:可以利用拓展KMP求出S的每一个后缀和T的最长公共前缀。假如当前最长公共前缀为k,就说明长度为k的前缀在S中出现了一次,并且这个k前缀不能构成k+1前缀。用一个cnt数组将各种长度前缀出现的次数记录下来。
来看一下第二个样例。
abababab
aba
首先将样例翻转,得到
babababa
aba
拓展KMP求出的extend数组值如下
extend[0] = 0
extend[1] = 3
extend[2] = 0
extend[3] = 3
extend[4] = 0
extend[5] = 3
extend[6] = 0
extend[7] = 1
所以cnt数组值为
cnt[1] = 1
cnt[2] = 0
cnt[3] = 3
现在我们要根据cnt数组来求T的前缀在S中出现的次数了。
长度为3的前缀:出现了3次不解释。
长度为2的前缀:在长度为3的前缀中出现过3次,不能构成3前缀的2前缀数目(即cnt[2])等于0,所以2前缀出现了3次。
长度为1的前缀:在长度为2的前缀中出现了3次,不能构成2前缀的1前缀数目(即cnt[1])等于1,所以1前缀出现了4次。
ok,解决问题!
KMP:和拓展KMP相似,主要思路就是求出不能构成k + 1前缀的k前缀数目。相信大家都写过用KMP统计T串在S串中出现次数的题目吧。和那个题的代码相似,KMP匹配的时候,我们只需要在失配和匹配完成的时候记录一下即可。设失配的时候已经匹配的长度为k,那么这个k前缀不能构成k + 1前缀。
给出KMP的代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N = 1000005;
const int MOD = 1000000007;
char S[N], T[N];
int lens, lent;
int Next[N];
int cnt[N];
void getNext()
{
int i = 0;
int k = -1;
Next[0] = -1;
while (i < lent)
if (k == -1 || T[i] == T[k])
Next[++i] = ++k;
else
k = Next[k];
}
void KMP_count()
{
int i = 0;
int j = 0;
getNext();
while (i <= lens) // 注意不能是<,否则最后一个模式会遗漏,用题目中的第二个样例debug一下就知道了。
{
if (j == -1 || S[i] == T[j])
{
i++;
j++;
}
else // 失配的时候记录一下
{
cnt[j]++;
j = Next[j];
}
if (j == lent) // 匹配完成的时候记录一下
{
cnt[j]++;
j = Next[j];
}
}
}
int main()
{
//freopen("test.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int Case;
scanf("%d", &Case);
while (Case--)
{
scanf("%s%s", S, T);
lens = strlen(S);
lent = strlen(T);
reverse(S, S + lens);
reverse(T, T + lent);
memset(cnt, 0, sizeof(cnt));
KMP_count();
LL tmp = cnt[lent];
LL ans = cnt[lent] * lent;
for (int i = lent - 1; i >= 1; i--)
{
tmp = (tmp + cnt[i]) % MOD;
ans = (ans + tmp * i) % MOD;
}
printf("%I64d\n", ans);
}
return 0;
}