题目概述
给出一个字符串,定义一个回文子串的权值为长度 × 出现次数。求最大权值。
解题报告
求回文子串可以用Manacher,每当找到一个回文子串,就用后缀数组求出其出现次数。因为是求子串出现次数,所以构造 height 再二分就可以 O(log2n) 查找。
本质不同回文子串的个数是 O(n) 的,所以效率是 O(nlog2n) 。
由于太蒟蒻,代码自带大常数,跑了19936ms……勉强卡过……
示例程序
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxl=600000,Log=20;
int n,p[maxl+5];LL ans;char s[maxl+5],now[maxl+5];
int ha[maxl+5],t[maxl+5],rk[maxl+5],SA[maxl+5],RMQ[maxl+5][Log+5];
inline void Sort(int n,int m){
for (int i=1;i<=m;i++) ha[i]=0;for (int i=1;i<=n;i++) ha[rk[i]]++;
for (int i=1;i<=m;i++) ha[i]+=ha[i-1];for (int i=n;i;i--) SA[ha[rk[t[i]]]--]=t[i];
}
void make_SA(){
int m=256;for (int i=1;i<=n;i++) rk[i]=s[i],t[i]=i;Sort(n,m);
for (int k=1,p=0;p<n;k<<=1,m=p){
p=0;for (int i=n-k+1;i<=n;i++) t[++p]=i;for (int i=1;i<=n;i++) if (SA[i]>k) t[++p]=SA[i]-k;
Sort(n,m);memcpy(t,rk,sizeof(t));rk[SA[1]]=p=1;
for (int i=2;i<=n;rk[SA[i++]]=p) p+=(t[SA[i-1]]==t[SA[i]]&&t[SA[i-1]+k]==t[SA[i]+k])^1;
}
for (int i=1,k=0;i<=n;i++){
if (k) k--;if (rk[i]==1) continue;int j=SA[rk[i]-1];
while (s[i+k]==s[j+k]) k++;RMQ[rk[i]][0]=k;
}
for (int j=1;(1<<j)<=n;j++)
for (int i=2;i<=n-(1<<j)+1;i++)
RMQ[i][j]=min(RMQ[i][j-1],RMQ[i+(1<<j-1)][j-1]);
}
inline int getMIN(int L,int R) {int j=log2(R-L+1);return min(RMQ[L][j],RMQ[R-(1<<j)+1][j]);}
inline void Find(int x,int len){
int L=x+1,R=n,r;
if (RMQ[x+1][0]<len) R=x; else
for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1))
if (len<=getMIN(x+1,mid)) L=mid+1; else R=mid-1;
r=R;L=1;R=x-1;
if (RMQ[x][0]<len) L=x; else
for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1))
if (len<=getMIN(mid+1,x)) R=mid-1; else L=mid+1;
ans=max(ans,(LL)(r-L+1)*len);
}
void Manacher(){
int pos=0,R=0;
for (int i=1;i<=n*2+1;i++){
if (i<R) p[i]=min(p[pos*2-i],R-i); else p[i]=1;
while (1<=i-p[i]&&i+p[i]<=n*2+1&&now[i-p[i]]==now[i+p[i]]) p[i]++;
if (i+p[i]>R){
for (int r=R+(R&1),l;r<i+p[i];r+=2) l=i*2-r,Find(rk[l>>1],(r-l>>1)+1);
pos=i;R=i+p[i];
}
}
}
int main(){
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%s",s+1);n=strlen(s+1);
for (int i=1;i<=n;i++) now[i*2-1]='%',now[i*2]=s[i];now[n*2+1]='%';
return make_SA(),Manacher(),printf("%lld\n",ans),0;
}