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;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/13295881.html