bzoj3676 [Apio2014]回文串
原题地址:
http://www.lydsy.com/JudgeOnline/problem.php?id=3676
题意:
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。
数据范围
1≤字符串长度≤300000
题解:
一篇回文自动机讲解
惨啊惨啊,昔日Apio2014的题沦为裸题…
当然,我这个串出现次数就是我这个节点的cnt和所有包含我的节点的cnt和,
所谓
父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
注意:
ch[tmp][c]=tail;
放在后边,
否则可能在上面找fail时又找到了自己 。
例如0和1的fail是互指的。
1加了v节点,fail[1]=0,于是从0开始找f又找到1,这时候1的ch[c]已经是v了,于是变成了自己指向自己。
等有时间写了SAM+manacher再来UPD一发吧。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300005;
int n=0;
char s[N];
struct PAM
{
int ch[N][26],len[N],fail[N],cnt[N],S[N],num[N],tail,last;
void init()
{
len[0]=0; len[1]=-1; fail[0]=1; last=0; S[0]=-1; tail=1;
}
void insert(int c)
{
S[++n]=c; int tmp=last;
while(S[n-len[tmp]-1]!=S[n]) tmp=fail[tmp];
if(!ch[tmp][c])
{
len[++tail]=len[tmp]+2;
int f=fail[tmp];
while(S[n-len[f]-1]!=S[n]) f=fail[f];
fail[tail]=ch[f][c];
num[tail]=num[fail[tail]]+1;
ch[tmp][c]=tail;
}
cnt[ch[tmp][c]]++;
last=ch[tmp][c];
}
}pam;
int main()
{
scanf("%s",s+1); int lens=strlen(s+1);
pam.init();
for(int i=1;i<=lens;i++) pam.insert(s[i]-'a');
LL ret=0;
for(int i=pam.tail;i>=0;i--)
{
pam.cnt[pam.fail[i]]+=pam.cnt[i];
ret=max(ret,1LL*pam.len[i]*pam.cnt[i]);
}
printf("%lld\n",ret);
return 0;
}