题目链接:https://vjudge.net/problem/Gym-101981M
题意:求 s的一个子串后面加上t的前缀为回文串并且满足|s| > |t| 的个数
题解:因为牵扯到了前缀和子串的问题,大体就能想到要用到扩展kmp,我们把s倒置,那么问题就转化为了,对于s串的每一个位置pos的后缀和t前缀的公共长度 * 以pos-1为结尾的回文串个数 的总和,求公共长度就是扩展kmp来求,以某个位置结尾的回文串个数可以用回文树来求,也可以用马拉车来求。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
char s[N], t[N];
int lens, lent;
int nex[N], ex[N];
void get_nex() {
int i = 0, j, pos, len = lent;
nex[0] = len;
while(t[i] == t[i + 1] && i + 1 < len) i++;
nex[1] = i;
pos = 1;
for(i = 2; i < len; i++) {
if(nex[i - pos] + i < nex[pos] + pos)
nex[i] = nex[i - pos];
else {
j = nex[pos] + pos - i;
if(j < 0) j = 0;
while(i + j < len && t[j] == t[j + i]) j++;
nex[i] = j;
pos = i;
}
}
}
void ex_kmp() {
int i = 0, j, pos, l1 = lent, l2 = lens;
get_nex();
while(t[i] == s[i] && i < l1 && i < l2) i++;
ex[0] = i;
pos = 0;
for(i = 1; i < l2; i++) {
if(nex[i - pos] + i < ex[pos] + pos)
ex[i] = nex[i - pos];
else {
j = ex[pos] + pos - i;
if(j < 0) j = 0;
while(i + j < l2 && j < l1 && t[j] == s[i + j]) j++;
ex[i] = j;
pos = i;
}
}
}
char str[N * 2];
int p[N * 2];
int sum[N * 2];
int main() {
scanf("%s %s", s, t);
lens = strlen(s);
lent = strlen(t);
for(int i = 0; i < lens / 2; i++) swap(s[i], s[lens - 1 - i]);
ex_kmp();
str[0] = '0'; str[1] = '#';
for(int i = 0; i< lens; i++) {
str[i * 2 + 2] = s[i];
str[i * 2 + 3] = '#';
}
str[lens * 2 + 2] = '1';
int maxx = 0, id;
for(int i = 2; i <= lens * 2 + 1; i++) {
p[i] = maxx > i ? min(p[id - (i - id)], maxx - i) : 1;
while(str[i + p[i]] == str[i - p[i]])p[i]++;
if(i + p[i] > maxx) {
maxx = i + p[i];
id = i;
}
// cout << i << " " << i + p[i] - 1 << endl;
sum[i - 1]--;
sum[i + p[i] - 1]++;
}
for(int i = lens * 2 + 1; i >= 0; i--) {
sum[i] += sum[i + 1];
}
ll ans = 0;
ll cnt1, cnt2;
int cnt;
int l, r, mid;
int pos;
for(int i = 0; i < lens; i++) {
cnt1 = ex[i];
l = 0, r = i * 2 + 1;
pos = r;
cnt2 = sum[r - 1];
ans += cnt1 * cnt2;
}
printf("%lld\n", ans);
return 0;
}