=== ===
这里放传送门
=== ===
题解
因为最长的双回文串能够被拆成两个回文串,但这两个回文串不一定都是原串中极长的回文串,也就是它可能由于重叠被缩掉了一部分。然而可以发现如果两个回文串a和b的重叠长度是d,那么它们两个形成的双回文串的长度一定是
a∗b−2∗d
,和拆分方式没有关系。
那么如果能求出对于一个位置来说左端能覆盖到它的最长回文串和右端能覆盖到它的最长回文串,答案一定出在这样的组合里面,因为如果用来构造双回文串的不是能覆盖到这个位置的最长回文串,那么替换一下一定更优。
求覆盖到这个位置的最长回文串可以转化成求覆盖到这个位置的最远回文中心。可以发现这个是单调的,线性扫一遍就可以预处理出来。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int p[200010],len,ans,tot,L[200010],R[200010];
char tmp[100010],S[200010];
void trans(){
int cnt=strlen(tmp);
S[0]='$';len=0;
for (int i=0;i<cnt;i++){
S[++len]='#';S[++len]=tmp[i];
}
S[++len]='#';
}
void manacher(){
int Max=0,pos=0;
for (int i=1;i<=len;i++){
if (Max>i) p[i]=min(p[2*pos-i],Max-i);
else p[i]=1;
while (S[i-p[i]]==S[i+p[i]]) ++p[i];
if (p[i]+i>Max){Max=i+p[i];pos=i;}
}
}
int main()
{
gets(tmp);trans();
manacher();
for (int i=1,ptr=1;i<=len;i++)
if (p[i]>1){
int r=i+p[i]-1;
while (ptr<=r) L[ptr++]=i;
}
for (int i=len,ptr=len;i>=1;i--)
if (p[i]>1){
int l=i-p[i]+1;
while (ptr>=l) R[ptr--]=i;
}
for (int i=1;i<=len;i++){
int l=R[i]-p[R[i]]+1,r=L[i]+p[L[i]]-1,dec;
dec=(r-l+1);l=L[i];r=R[i];
ans=max(ans,(p[l]-1)+(p[r]-1)-(dec/2)*2);
}
printf("%d\n",ans);
return 0;
}