P4555 最长双回文串
https://www.luogu.com.cn/problem/P4555
https://darkbzoj.tk/problem/2565
\(T=XY\),其中 \(X,Y\) 均为回文串,求 \(T\) 的最大长度
首先把每两个原有字符的间隙填充上其他字符,然后可以用 manacher 求出每个点的最大回文半径 \(len_i\),此时这个回文半径对应的回文串的实际长度是 \(len_i-1\)
因为要求的是两个回文串拼起来,所以可以求 \(l_i,r_i\) 分表表示以 \(i\) 开头,结尾的回文串最大长度,答案就是 \(\max\{l_i+r_i\}\)
当求出 \(len_i\) 时,这样更新:\(l_{i-len_i+1}=\max(l_{i-len_i+1},len_i-1),r_{i+len_i-1}=\max(r_{i+len_i-1},len_i-1)\)
注意这里 \(l,r\) 的下标是以填充后的字符串下标为准,而值是在原有字符串中的长度,也就是此时的 \(len_i-1\)
而我们要考虑的也只是当 \(s_i\) 是填充进来的字符时,\(l_i,r_i\) 的值,因为此时他才满足向左向右都是原串的一个回文子串
但,这样只是更新了最大长度,比如对于 \(len_i'<len_i\),\(l_{i-len_i'+1}\) 也可以用 \(len_i'-1\) 来更新,但这里并没有更新到
所以做完 manacher 以后,还要分别对 \(l,r\) 处理:对于每一个填充进来的 \(s_i\),\(l_i=\max(l_i,l_{i-2}-2),r_i=\max(r_i,r_{i+2}-2)\)
也就是以 \(i\) 开头的,就考虑一下以 \(i-2\) 开头的(上一个填充进来的字符),用它的长度减去 \(2\)(减去一头一尾)来更新。以 \(i\) 结尾的也是同理
最后考虑这些填充进来的 \(s_i\),取 \(\max\{l_i+r_i\}\) 为答案
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
char in[100005],s[200005];
int n;
int len[200005];
int l[200005],r[200005];
int main(){
scanf("%s",in+1);
int lenin=std::strlen(in+1);
s[0]=0;s[1]=2;n=1;
for(reg int i=1;i<=lenin;i++) s[++n]=in[i],s[++n]=2;
s[n+1]=1;
int pos=0,max=0;
for(reg int i=1;i<=n;i++){
if(pos+max-1<i) len[i]=1;
else len[i]=std::min(len[pos+pos-i],pos+max-i);
while(s[i+len[i]]==s[i-len[i]]) len[i]++;
if(i+len[i]-1>=pos+max-1) pos=i,max=len[i];
l[i-len[i]+1]=std::max(l[i-len[i]+1],len[i]-1);
r[i+len[i]-1]=std::max(r[i+len[i]-1],len[i]-1);
}
for(reg int i=1;i<=n;i+=2) l[i]=std::max(l[i],l[i-2]-2);
for(reg int i=n;i>0;i-=2) r[i]=std::max(r[i],r[i+2]-2);
reg int ans=0;
for(reg int i=1;i<=n;i+=2) ans=std::max(ans,(l[i]&&r[i])*(l[i]+r[i]));
printf("%d",ans);
return 0;
}