传送门
题面:
3676: [Apio2014]回文串
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 3755 Solved: 1755
[Submit][Status][Discuss]
Description
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。
Input
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
Output
输出一个整数,为逝查回文子串的最大出现值。
Sample Input
【样例输入l】
abacaba
【样例输入2]
www
Sample Output
【样例输出l】
7
【样例输出2]
4
HINT
一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。
在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:
● a出现4次,其出现值为4:1:1=4
● b出现2次,其出现值为2:1:1=2
● c出现1次,其出现值为l:1:l=l
● aba出现2次,其出现值为2:1:3=6
● aca出现1次,其出现值为1=1:3=3
●bacab出现1次,其出现值为1:1:5=5
● abacaba出现1次,其出现值为1:1:7=7
故最大回文子串出现值为7。
【数据规模与评分】
数据满足1≤字符串长度≤300000。
题目分析:
回文树模板题。首先需要了解回文树的含义。
知道了回文树的含义后,我们很明显可以发现,事实上题目中要我们求的就是长度为i的字符串的本质不同的回文字串的个数num,用num*i即为最终的答案。
因此我们只需要将字符串建一颗回文树,之后按fail边自下而上更新一下即可。
最后只需要输出它们当中最大的。
代码:
#include <bits/stdc++.h>
#define maxn 300005
using namespace std;
char str[maxn];
typedef long long ll;
struct PAM{//回文树
int next[maxn][26],fail[maxn],len[maxn],cnt[maxn],S[maxn];
int id,n,last;
int newnode(int x){
for(int i=0;i<26;i++){
next[id][i]=0;
}
cnt[id]=0;
len[id]=x;
return id++;
}
void init(){
id=0;
newnode(0);
newnode(-1);
fail[0]=1;
S[0]=-1;
last=n=0;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n]) x=fail[x];
return x;
}
void Insert(int c){
c-='a';
S[++n]=c;
int cur=getfail(last);
if(!next[cur][c]){
int now=newnode(len[cur]+2);
fail[now]=next[getfail(fail[cur])][c];
next[cur][c]=now;
}
last=next[cur][c];
cnt[last]++;
}
void getsum(){//自下向上更新
for(int i=id-1;i>=0;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int main()
{
scanf("%s",str);
pam.init();
int len=strlen(str);
for(int i=0;i<len;i++){
pam.Insert(str[i]);
}
pam.getsum();
ll res=0;
for(int i=2;i<pam.id;i++){
res=max(res,1ll*pam.len[i]*pam.cnt[i]);
}
cout<<res<<endl;
}