回文树(回文自动机)学习小记

参考资料:

翁文涛在2017年国家候选队的论文。

前言:

感觉这个东西比后缀自动机好理解。

博主是看wwt的论文学的:

wwt给出的这种不用打通配符的方法更加简洁。

定义:

回文树有两个根。

分别为even,odd,长度分别是0和-1。

len为一个点代表的字符串的实际长度。

fail为这个点失配后的最长回文后缀。

go是自动机的边。

构建:

fail[even]=odd

用和后缀自动机一样的增量法。

考虑当前的串是t,在t后面加一个x,为tx。

若t的fail链上有一p,p是回文串。

如果有s[n-|p|-1]=s[n],那么xpx就是合法的回文后缀了。

于是找到最大的满足条件的p就好了。

如果不存在这个xpx这个串,显然需要把它加到树里,并且维护一下它的fail。

复杂度分析:

势能分析,博主不会。

时间复杂度O(n log n),空间复杂度O(n *|str|),str为可能的字符集大小。

例题:

【APIO2014】回文串

这题有很多解法,最简单的一种就是直接上回文树。

张天杨的论文里也提到了这题,可以建后缀自动机,然后反串丢上去匹配。

但是好像当时的标算是用manacher求回文串,后缀数组(后缀自动机)求出现次数。

Code:

#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define ll long long
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int N = 3e5 + 5;

char s[N]; ll ans;

struct hwt {
    int fail[N], to[N][26], cnt[N], len[N], tot, last, n;
    void First() {
        tot = 1;
        len[1] = -1;
        fail[0] = 1;
        last = 0;
        s[0] = -1;
    }
    int Getfail(int p) {
        while(s[n - len[p] - 1] != s[n]) p = fail[p];
        return p;
    }
    void add(char c) {
        n ++;
        int cur = Getfail(last);
        if(!to[cur][c]) {
            int p = ++ tot;
            len[p] = len[cur] + 2;
            fail[p] = to[Getfail(fail[cur])][c];
            to[cur][c] = p;
        }
        last = to[cur][c];
        cnt[last] ++;
    }
    void Sum() {
        fd(i, tot, 1) {
            cnt[fail[i]] += cnt[i];
            ans = max(ans, (ll)cnt[i] * len[i]);
        }
    }
} str;

int main() {
    freopen("palindrome.in","r",stdin);
    freopen("palindrome.out","w",stdout);
    str.First();
    scanf("%s", s + 1); int l = strlen(s + 1);
    fo(i, 1, l) str.add(s[i] - 'a');
    str.Sum();
    printf("%lld", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值