初探 回文树

来源:http://victorwonder.blog.uoj.ac/blog/146

优点:

  • 代码好写,易于调试
  • 由于是树形,功能强大(不过由于回文串的限制,这类题目比较少)

简述

回文树是两棵树,每个节点代表一个回文串。第一棵树的点是长度为偶数的回文串,那第二棵肯定就是奇数的啦。
为了方便,第一棵树的根是一个长度为\(0\)的串,第二棵就是为\(-1\)的串,不要感到奇怪,就是\(-1\)
可以证明,最多只有\(n\)个结点(\(n\)是串的长度)。这个可以用manacher算法来证明。

如果某结点代表的是串ccabacc,那么它的父亲代表的串就是去掉前后两个字符cabac

每个点还有一个fail指针,表示这个串的后缀中最长的回文串,比如babab的fail指向babbab的指向b

现来讲讲如何构树,方法的思想和KMP,AC自动机没啥两样,看看代码就立马可以懂了。

模板题

apio2014 回文串
我目前刷了个rank7,相信随着这个算法的普及,我要往下跌了。

update

果不其然,现在已经跌到rank25。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = (int) 3e5 + 3;
typedef long long i64;

template <class T>
void relax(T &a, const T &b) {
    if (b > a) a = b;
}

int n;
char str[MAXN];

struct Node {
    Node* s[26];
    Node* fail;
    int len;
    int cnt;
};

Node mem[MAXN];
Node* curMem = mem;

Node* getFail(Node* x, int i) {
    while (i == x->len || str[i] != str[i - x->len - 1])
        x = x->fail;
    return x;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    scanf("%s", str);
    n = strlen(str);

    Node* root0 = curMem ++;
    Node* root1 = curMem ++;
    root0->fail = root1;
    root1->len = -1;

    Node* cur = root1;

    for (int i = 0; i < n; i ++) {
        int p = str[i] - 'a';
        cur = getFail(cur, i);
        if (! cur->s[p]) {
            Node* x = curMem ++;
            cur->s[p] = x;
            x->len = cur->len + 2;
            if (cur == root1) 
                x->fail = root0;
            else
                x->fail = getFail(cur->fail, i)->s[p];
        }
        cur = cur->s[p];
        cur->cnt ++;
    }

    i64 ans = 0;
    for (Node* pt = curMem - 1; pt >= mem; pt --) {
        if (pt == root0 || pt == root1) continue;
        pt->fail->cnt += pt->cnt;
        relax(ans, (i64) pt->len * pt->cnt);
    }
    cout << ans << endl;

    return 0;
}

转载于:https://www.cnblogs.com/wangck/p/4369865.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值