回文树模板题

题目链接 https://www.luogu.org/problem/P3649

题目描述

给你一个由小写拉丁字母组成的字符串 ss。我们定义 ss 的一个子串的存在值为这个子串在 ss 中出现的次数乘以这个子串的长度。

对于给你的这个字符串 ss,求所有回文子串中的最大存在值。

#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 = 300005;
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 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];
        cnt[last]++;
    }
    void count_cnt(){
        for(int i = p-1; i >= 0; i--){
            cnt[fail[i]] += cnt[i]; //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
        }
    }
}pam;

char st[maxn];
int main(){
    scanf("%s",st);
    int len = strlen(st);
    pam.init();
    for(int i = 0; i < len; i++){
       pam.add(st[i]);
    }
    pam.count_cnt();
    ll ma = 0;
    for(int i = 2; i < pam.p; i++){
        ma = max((ll)pam.len[i] * pam.cnt[i], ma);
    }
    cout << ma <<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/philo-zhou/p/11502526.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值