bzoj2342: [Shoi2011]双倍回文 回文自动机

bzoj2342: [Shoi2011]双倍回文

Description

Input

输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容。

Output

输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文子串不存在,则输出0。

Sample Input

16
ggabaabaabaaball

Sample Output

12

HINT

N<=500000

分析

我们先不考虑奇数。以一个节点为中心的双倍回文。
这个节点的左边回文串和右边回文串要一模一样。
我们可以用回文自动机简单地处理出一个节点往左的最长回文串。
对于一个节点往右的最长回文串,我们可以把整串倒过来再插入回文自动机。假设i正着插入对应回文自动机上的节点为in[i],倒着插入为out[i]
我们考虑回文自动机fail树上的一条路径,每一条路径上的所有节点都是以i为结尾的一个回文字符串。
考虑这个节点左右回文串一模一样,左右回文串在回文自动机上表示同一个节点。
所以就是in[i]和out[i]再fail树上的lca
最后考虑必须是偶数长度的回文串,我们只要在做自动机的时候记录某个节点在fail树上祖先中距离它最近的偶数回文串即可。
注意回文自动机上节点有0

代码

/**************************************************************
    Problem: 2342
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:1168 ms
    Memory:106708 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int N = 1e6 + 1e3;
long long ans;
int len[N], f[N], a[N], val[N], F[N], pos[N], last, sz, n, L;
map<int, int>ch[N];
int siz[N], to[N], pre[N], nxt[N], son[N], de[N], d[N], top;
void add(int u, int v) {to[++top] = v; nxt[top] = pre[u]; pre[u] = top;}

void dfs(int u) {
    de[u] = de[f[u]] + 1; siz[u] = 1; son[u] = -1;
    for(int i = pre[u]; i; i = nxt[i]) {
        dfs(to[i]);
        siz[u] += siz[to[i]];
        if(!(~son[u]) || siz[to[i]] > siz[son[u]]) son[u] = to[i];
    }
}
void dfs(int u, int chain) {
    d[u] = chain; if(!(~son[u])) return;
    dfs(son[u], chain);
    for(int i = pre[u]; i; i = nxt[i])
    if(to[i] != son[u])
        dfs(to[i], to[i]);
}
int lca(int u, int v) {
    for(;d[u] != d[v]; u = f[d[u]])
        if(de[d[u]] < de[d[v]]) swap(u, v);
    return de[u] > de[v] ? v : u;
}

void PDT_pre() {
    f[last = sz = 0] = 1; add(1, 0);
    len[++sz] = a[n = 0] = F[1] = F[0] = -1;
}
void Extend(int n, int c) {
    int p, np, x;
    for(p = last;a[n - len[p] - 1] != a[n];p = f[p]) ;
    if(!ch[p][c]) {
        len[np = ++sz] = len[p] + 2;
        for(x = f[p]; a[n - len[x] - 1] != a[n]; x = f[x]) ;
        f[np] = ch[x][c]; ch[p][c] = np;
        F[np] = len[np] & 1 ? F[f[np]] : np;
        add(f[np], np);
    }
    pos[n] = last = ch[p][c];
}

void work() {
    char ch = getchar();
    while(ch < 'a' || ch > 'z') ch = getchar();
    for(;ch >= 'a' && ch <= 'z'; ch = getchar()) a[++n] = ch - 'a';
    a[n + 1] = 26; a[n + 2] = 27; L = n + 1 << 1;
    for(int i = 1;i <= n; ++i) a[L - i + 1] = a[i];
    for(int i = 1;i <= L; ++i) Extend(i, a[i]);
} 

int main() {
    int ans = 0; PDT_pre(); work();
    dfs(1); dfs(1, 1);
    for(int i = 1;i <= n; ++i) {
        int x = lca(pos[i], pos[L - i]); 
        if(F[x] != -1) ans = max(ans, (len[F[x]] << 1));
    }
    printf("%d\n", ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值