题目链接: A Secret
大致题意:
给出两个字符串, 统计串2的后缀子串在串1中的出现次数. 假设串2中后缀子串长度为i时在串1中出现x次, 则fact(s2, i) = i * x. 最终输出 fact(s2, i)的总和(1 <= i <= |s2|) .
题目感觉不太好叙述, 麻烦自己看吧
解题思路:
对于字符串匹配, 我们很容易想到kmp, 但是kmp的匹配和这个题的思路有些不同, kmp是计算前缀, 而本题则是计算后缀. 所以我们不妨将s1与s2都进行翻转, 这样我们就可以得到kmp的模板串s1, 模式串s2.
现在我们发现, 在我们进行kmp时, 如果匹配到第j位失败(第j-1位成功匹配), 则s2的[1, j - 1]区间的前缀子串匹配的长度都应当+1. 这样我们发现, 我们可以通过后缀和优化, 从而在线性时间内得出结果.
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1E6 + 10, mod = 1E9 + 7;
char s1[N], s2[N];
int ne[N], cou[N]; //起初cou数组为以第i位匹配成功, i+1匹配失败时的情况数量
int main()
{
int t; cin >> t;
while (t--) {
memset(cou, 0, sizeof cou);
scanf("%s %s", s1 + 1, s2 + 1);
int l1 = strlen(s1 + 1), l2 = strlen(s2 + 1);
reverse(s1 + 1, s1 + 1 + l1); reverse(s2 + 1, s2 + 1 + l2);
for (int i = 2, j = 0; i <= l2; ++i) { //kmp处理ne数组
while (j && s2[i] != s2[j + 1]) j = ne[j];
if (s2[i] == s2[j + 1]) ++j;
ne[i] = j;
}
/* KMP */
int j = 0;
for (int i = 1; i <= l1; ++i) {
while (j && s1[i] != s2[j + 1]) cou[j]++, j = ne[j]; //匹配到j成功, j+1失败, 记录情况
if (s1[i] == s2[j + 1]) ++j;
}
while (j) cou[j]++, j = ne[j]; //收尾操作, 很重要
//求后缀和, 此时cou数组性质改变为串s2中长度为i的前缀子串匹配次数.
for (int i = l2 - 1; i >= 1; --i) cou[i] += cou[i + 1];
int res = 0; //计算res
for (int i = 1; i <= l2; ++i) res = (res + 1ll * cou[i] * i) % mod;
printf("%d\n", res);
}
return 0;
}