P4555 [国家集训队]最长双回文串 回文树(回文自动机)简单题

贴个题目链接:https://www.luogu.org/problem/P4555

题目:输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(∣X∣,∣Y∣≥1)且X和Y都是回文串。

输入输出样例

输入 #1
baacaabbacabb
输出 #1
12

说明/提示

【样例说明】

从第二个字符开始的字符串aacaabbacabb可分为aacaabbacabb两部分,且两者都是回文串。

思路,我们的回文自动机每次加进去一个字符都会更新last,last就是以这个字符为结尾能得到的最长回文串的节点号,那我们直接开个ans数组记录一下len【last】就是表示以这个字符为结尾的最长回文串长度。

搞两个回文树,一个正着跑,一个倒着跑,那么我们就可以ans1【i】+ans2【i+1】表示的意思就是以s【i】为X结尾,s【i+1】为Y开头(因为这个是倒着跑的,正着看就是以它为开头)最回文大长度。

#include <bits/stdc++.h>
#define met(a, b) memset(a, b, sizeof(a))
#define ll long long
#define ull unsigned long long
using namespace std;

const int maxn = 100005;
const int N = 26;
struct PAM{
    int ne[maxn][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[maxn];//fail指针,失配后跳转到fail指针指向的节点
    int cnt[maxn]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    int num[maxn]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
    int len[maxn];//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
    int S[maxn] ;//存放添加的字符
    int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。
    int n;//表示添加的字符个数。
    int p;//表示添加的节点个数。
    int ans[maxn];//以st【i】为结尾的最长回文子串的长度
    int newnode(int l){ //新建节点
        for(int i = 0; i < N; i++) ne[p][i] = 0;
        cnt[p] = num[p] = 0;
        len[p] = l;
        return p++;
    }
    void init(){ //初始化
        p = 0;
        newnode(0);
        newnode(-1);//顺序不能反
        last = 0;
        n = 0;
        S[n] =  -1; //防止越界
        fail[0] = 1;
    }
    int get_fail(int x){
        while(S[n - len[x] - 1] != S[n]) x = fail[x];
        return x;
    }
    void add(int c){
        c = c - 'a';
        S[++n] = c;
        int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置
        if(!ne[cur][c]){//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode(len[cur] + 2);//新建节点
            fail[now] =  ne[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转
            ne[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = ne[cur][c];
        ans[n] = len[last];
        cnt[last]++;
    }
    void count_cnt(){
        for(int i = p-1; i >= 0; i--){
            cnt[fail[i]] += cnt[i]; //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
        }
    }
}pam1, pam2;

char st[maxn];
int arr[10005];
int main(){
    scanf("%s",st);
    pam1.init();
    pam2.init();
    int len = strlen(st);
    for(int i = 0; i < len; i++)
        pam1.add((int)st[i]);
    for(int i = len-1; i >= 0; i--)
        pam2.add((int)st[i]);
    pam1.count_cnt();
    pam2.count_cnt();
    int ma = 0;
    for(int i = 1; i < len; i++){
        ma = max(ma, pam1.ans[i] + pam2.ans[len-i]);
    }
    cout << ma << endl;
    return 0;
}

 

posted @ 2019-09-10 23:17  philo_zhou  阅读(180)  评论(0编辑  收藏  举报