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;
}
posted @ 2024-01-05 08:37  5k_sync_closer  阅读(30)  评论(0编辑  收藏  举报  来源