[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;
}