题目链接 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; }