BZOJ3676[Apio2014]回文串——回文自动机

题目描述

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最 
大出现值。 

输入

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。 

输出

输出一个整数,为逝查回文子串的最大出现值。 

样例输入

【样例输入l】
abacaba

【样例输入2]
www

样例输出

【样例输出l】
7

【样例输出2]
4

提示

一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。 
在第一个样例中,回文子串有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。

 

回文串长度在建回文自动机时就可以求出,主要是求每个回文串的出现次数。我们在建回文自动机时记录每个点的权值表示原串以多少个位置为结尾的最长回文串为这个点所代表的的回文串。那么每个回文串的出现次数就是$fail$指针直接或间接地指向它的点的权值和,也就是$fail$树上这个点的子树权值和,求出$fail$树然后累加一下取最大值即可。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
char s[300010];
int n,p,q;
int tot;
int fail[300010];
int tr[300010][26];
int last;
int cnt[300010];
int len[300010];
ll ans;
int build(int x)
{
	len[++tot]=x;
	return tot;
}
int getfail(int x,int n)
{
	while(s[n-len[x]-1]!=s[n])
	{
		x=fail[x];
	}
	return x;
}
int main()
{
	scanf("%s",s+1);
	s[0]=-1;
	fail[0]=1;
	last=0;
	len[0]=0;
	len[1]=-1;
	tot=1;
	int size=strlen(s+1);
	for(int i=1;i<=size;i++)
	{
		int x=s[i]-'a';
		p=getfail(last,i);
		if(!tr[p][x])
		{
			q=build(len[p]+2);
			fail[q]=tr[getfail(fail[p],i)][x];
			tr[p][x]=q;
		}
		cnt[last=tr[p][x]]++;
	}
	for(int i=tot;i;i--)
	{
		cnt[fail[i]]+=cnt[i],ans=max(ans,1ll*cnt[i]*len[i]);
	}
	printf("%lld",ans);
}
posted @ 2019-02-22 14:16  The_Virtuoso  阅读(253)  评论(0编辑  收藏  举报