P5685 [JSOI2013] 快乐的 JYY 题解
简化题意:公共回文串计数。
对两个串分别建 PAM,然后同时遍历它们,每次只走它们共有的边,
此时能遍历到的点就是两个串的所有公共回文串,考虑每个点的贡献。
对 PAM 上每个点 $i$ 求出 $c_i$ 表示其出现次数,则能被同时遍历到的点对 $(i,j)$ 的贡献即为 $c_i\times c_j$。
#include <cstdio>
#include <algorithm>
using namespace std;
long long q;
struct T
{
char u[50050];
int n, o, z, l[50050], f[50050], d[50050], c[50050][26];
int F(int x)
{
while (u[n] != u[n - l[x] - 1])
x = f[x];
return x;
}
void I(int x)
{
++n;
int p = F(z);
if (!c[p][x])
l[c[p][x] = ++o] = l[p] + 2, f[c[p][x]] = p != 1 ? c[F(f[p])][x] : 0;
++d[z = c[p][x]];
}
void D()
{
for (int i = o; i >= 2; --i)
d[f[i]] += d[i];
}
} X, Y;
void D(int x, int y, int d)
{
if (x >= 2 && y >= 2)
q += 1ll * X.d[x] * Y.d[y];
for (int i = 0; i < 26; ++i)
if (X.c[x][i] && Y.c[y][i])
D(X.c[x][i], Y.c[y][i], d + 2);
}
int main()
{
X.l[X.f[0] = X.o = 1] = -1;
Y.l[Y.f[0] = Y.o = 1] = -1;
scanf("%s%s", X.u + 1, Y.u + 1);
for (int i = 1; X.u[i]; ++i)
X.I(X.u[i] - 'A');
for (int i = 1; Y.u[i]; ++i)
Y.I(Y.u[i] - 'A');
X.D();
Y.D();
D(0, 0, 0);
D(1, 1, -1);
printf("%lld", q);
return 0;
}