[回文自动机][树形DP] CodeChef PALPROB

Solution

建出回文自动机,每个节点再维护一个 halfu 指针,指向长度不超过 lenu2 的最长回文后缀。
树形DP出每个点的 ness 表示回文指数,答案就是 nessu|rightu|
fail 链上的节点的 lenv 一定是递减的。可以倍增。
u 在后缀自动机上的父亲为p halfu 一定是 halfp fail 链上某一节点的儿子。暴力找就可以了。
口胡一波复杂度
设势能函数 Φ(s) 等于字符串 s 的不超过lens2的回文后缀的长度。显然有 Φ(sc)Φ(s)+1 。在 halfp fail 链上爬一步, Φ(sc) 减小 1 ,并且始终会有Φ(sc)0,所以势能总共只会增加 O(|s|) ,也总共只会减少 O(|s|) ,因此在 fail 链上跳的复杂度是 O(|s|) 的。
啊膜改了论文里的回文自动机的复杂度分析。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 101010;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline void read(char *s) {
    static char c; int len = 0;
    for (c = get(); c < 'a' || c > 'z'; c = get());
    for (; c >= 'a' && c <= 'z'; c = get()) s[len++] = c;
    s[len] = 0;
}

int ch[N][30];
int par[N], len[N];
int rgt[N], half[N], ness[N];
int last, Tcnt;
int n, test;
long long ans;
char s[N];

inline void Init(void) {
    memset(ch, 0, sizeof ch);
    memset(rgt, 0, sizeof rgt);
    memset(half, 0, sizeof half);
    par[0] = par[1] = 1;
    half[0] = half[1] = 1;
    len[1] = -1;
    Tcnt = 1; last = 0;
}
inline void Extend(int pos) {
    int p = last, key = s[pos];
    while (s[pos - len[p] - 1] != key) p = par[p];
    if (!ch[p][key]) {
        int np = ++Tcnt, q = par[p];
        len[np] = len[p] + 2;
        while (s[pos - len[q] - 1] != key) q = par[q];
        par[np] = ch[q][key]; q = half[p]; 
        while (len[ch[q][key]] > len[np] / 2 || s[pos - len[q] - 1] != key) q = par[q];
        half[np] = ch[q][key];
        ch[p][key] = np;
    }
    last = ch[p][key]; ++rgt[last];
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(test);
    while (test--) {
        read(s + 1); s[0] = -1;
        n = strlen(s + 1); Init();
        for (int i = 1; i <= n; i++) {
            s[i] -= 'a'; Extend(i);
        }
        for (int i = 2; i <= Tcnt; i++)
            if (len[half[i]] == len[i] / 2)
                ness[i] = 1 + ness[half[i]];
            else ness[i] = 1;
        ans = 0;
        for (int i = Tcnt; ~i; i--) {
            rgt[par[i]] += rgt[i];
            ans += (long long)ness[i] * rgt[i];
        }
        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值