BZOJ 3676 回文串
题意:给你一个串,回文串有个价值,定义为回文串的长度乘以次数,求最大值
思路:偷一份模板,附聚聚传送门
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; inline LL read() { LL x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn = 300010*2; const int ALP = 26; struct Palindromic_Tree{ ///init,把每个字符add进PAM中,最后做一次Count int next[maxn][ALP];//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<ALP;++i)next[p][i]=0; cnt[p]=0;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){//和KMP一样,失配后找一个尽量最长的 while(S[n-len[x]-1]!=S[n])x=fail[x]; return x; } void add(int c){ c-='a'; S[++n]=c; int cur=get_fail(last);//通过上一个回文串找这个回文串的匹配位置 if(!next[cur][c]){//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 int now=newnode(len[cur]+2);//新建节点 fail[now]=next[ get_fail(fail[cur]) ][c];//和AC自动机一样建立fail指针,以便失配后跳转 next[cur][c]=now ; num[now]=num[fail[now]]+1; } last=next[cur][c]; cnt[last]++; } void Count(){ for(int i=p-1;i>=0;--i)cnt[fail[i]]+=cnt[i]; //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串! } }pam; char s[maxn]; int main(){ scanf("%s",s); int len=strlen(s); pam.init(); for(int i=0;i<len;i++){ pam.add(s[i]); } pam.Count(); LL ans=0; for(int i=2;i<pam.p;i++){ ans=max(ans,(LL)pam.len[i]*pam.cnt[i]); } cout<<ans<<endl; return 0; }