[APIO2014]回文串

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

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

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

Sample Input 1
abacaba

Sample Output 1
7

Sample Input 2
www

Sample Output 2
4

HINT
\(\lvert s \rvert\) 表示字符串 s 的长度。
数据满足1≤ \(\lvert s\rvert\) ≤300000。


SA+Manacher

首先对原串处理出Height数组,同时求出以每个点作为回文中心所能扩张的最大长度

然后暴力枚举,对于每个作为回文中心的点,枚举回文串长度,然后在Height数组上二分,求出L,R,那么R-L+1就是出现次数

然后这样会TLE

考虑一个贪心的思想,对于每个作为回文串最左/右端的点,我们直接使用最大扩张长度即可

至于正确性?如果存在较小的长度使得其可以出现两次,那么我们必然可以在第二次出现的地方枚举到这种情况

然后注意求区间Height最小值,不能用线段树(可能会TLE),应该使用RMQ,复杂度为\(O(n\log n)\)

(然而我代码里写的是作为回文中心的……随手Hack,但是过了)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1;char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=3e5;
int n,m=26;
bool ck(int *r,int x,int y,int l){return r[x]==r[y]&&r[x+l]==r[y+l];}
void Suffix_sort(int *A,int *SA){
	static int sum[N+10],buf[N+10];
	memset(buf,0,sizeof(buf));
	int *x=A,*y=buf;
	for (int i=1;i<=m;i++)	sum[i]=0;
	for (int i=1;i<=n;i++)	sum[x[i]]++;
	for (int i=1;i<=m;i++)	sum[i]+=sum[i-1];
	for (int i=n;i>0;i--)	SA[sum[x[i]]--]=i;
	for (int l=1,p=0,top=0;p<n;l<<=1,m=p,top=0){
		for (int i=n-l+1;i<=n;i++)	y[++top]=i;
		for (int i=1;i<=n;i++)	if (SA[i]>l)	y[++top]=SA[i]-l;
		for (int i=1;i<=m;i++)	sum[i]=0;
		for (int i=1;i<=n;i++)	sum[x[y[i]]]++;
		for (int i=1;i<=m;i++)	sum[i]+=sum[i-1];
		for (int i=n;i>0;i--)	SA[sum[x[y[i]]]--]=y[i];
		swap(x,y),x[SA[1]]=p=1;
		for (int i=2;i<=n;i++)	x[SA[i]]=ck(y,SA[i-1],SA[i],l)?p:++p;
	}
}
int A[N+10],SA[N+10],Rank[N+10],H[N+10],p[(N<<1)+10];
void Get_height(char *s){
	for (int i=1,k=0,j;i<=n;H[Rank[i++]]=k)
	for (k?k--:0,j=SA[Rank[i]-1];s[i+k]==s[j+k];k++);
}
void Manacher(char *s){
	static char T[(N<<1)+10];
	for (int i=1;i<=n;i++)	T[i<<1]=s[i],T[i<<1|1]='#';
	int len=n<<1|1;
	T[0]='%',T[1]='#',T[len+1]='$';
	int Max=0,ID=0;
	for (int i=1;i<=len;i++){
		p[i]=Max>i?min(p[ID*2-i],Max-i):1;
		while (T[i+p[i]]==T[i-p[i]])	p[i]++;
		if (i+p[i]>Max)	Max=i+p[ID=i];
	}
}
int rmq[20][N+10],g[N+10];
void RMQ(){
	for (int i=1;i<=n;i++)	rmq[0][i]=H[i];
	for (int j=1;j<20;j++)
		for (int i=1;i<=n;i++)
			if (i+(1<<j)-1<=n)
				rmq[j][i]=min(rmq[j-1][i],rmq[j-1][i+(1<<(j-1))]);
	for (int i=2;i<=n;i++)	g[i]=g[i-1]+(2<<g[i-1]<=i);
}
bool check(int l,int r,int k){
	int jp=g[r-l],tmp=min(rmq[jp][l+1],rmq[jp][r-(1<<jp)+1]);
	return tmp>=k;
}
int work(int x,int len){
	int L,R,l,r;
	l=1,r=x-1;
	while (l<=r){
		int mid=(l+r)>>1;
		if (check(mid,x,len))	r=mid-1;
		else	l=mid+1;
	}L=l;
	l=x+1,r=n;
	while (l<=r){
		int mid=(l+r)>>1;
		if (check(x,mid,len))	l=mid+1;
		else	r=mid-1;
	}R=r;
	return R-L+1;
}
int main(){
	static char s[N+10];
	scanf("%s",s+1);
	n=strlen(s+1);
	for (int i=1;i<=n;i++)	A[i]=s[i]-'a'+1;
	Suffix_sort(A,SA);
	for (int i=1;i<=n;i++)	Rank[SA[i]]=i;
	Get_height(s),RMQ();
	Manacher(s);
	ll Ans=0;
	for (int i=1;i<=(n<<1|1);i++){
		if (p[i]==1)	continue;
		int x=(i>>1)-(p[i]>>1)+1;
		Ans=max(Ans,1ll*work(Rank[x],p[i]-1)*(p[i]-1));
	}
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2019-03-22 15:03  Wolfycz  阅读(265)  评论(0编辑  收藏  举报