P4555 [国家集训队]最长双回文串

链接:

P4555


题意:

在字符串 \(S\) 中找出两个相邻非空回文串,并使它们长度之和最大。


分析:

直接使用马拉车算法求出每个点扩展的回文串。如果枚举两个回文串显然会超时,我们考虑切割一个长串,即枚举切割点,只需枚举每个 \(\#\) 即可,但为了保证两个串都非空,所以最左和最右的 \(\#\) 不能枚举。然后我们需要找到最靠左的回文串中心 \(L\) 使得该回文串包括该点,以及最靠右的回文串中心 \(R\) 使得该回文串包括该点,发现两个回文串长度之和就是 \(R-L\)。同时我们发现当切割点向右移动,\(L\) 的位置是单调增的,当切割点向左移动,\(R\) 的位置是单调减的。所以我们可以双指针维护出每个切割点位置对应的 \(L\)\(R\)。于是就做完了。


代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=2e5+5;
string t="@#";int n;
inline void gett(){
	char c=getchar();
	while(c>'z'||c<'a')c=getchar();
	while(c>='a'&&c<='z')t+=c,t+="#",c=getchar();
	n=t.length();
}
int hm[N],mr,mid;
inline void match(){
	for(int i=1;i<n;i++){
		hm[i]=(mr>i)?min(hm[(mid<<1)-i],mr-i):1;
		while(t[i+hm[i]]==t[i-hm[i]])hm[i]++;
		if(i+hm[i]>mr)mr=i+hm[i],mid=i;
	}
}
int ans,now;
int l[N],r[N];
signed main(){
	gett();
	match();
	now=1;
	for(int i=1;i<n;i++){
		while(i>now+hm[now]-1)now++;
		l[i]=now;//最靠左的回文串中心L
	}
	now=n;
	for(int i=n-1;i>=1;i--){
		while(i<now-hm[now]+1)now--;
		r[i]=now;//最靠右的回文串中心R 
	}
	for(int i=3;i<n-2;i+=2)
		ans=max(r[i]-l[i],ans);
	cout<<ans;
	return 0;
}

posted @ 2021-10-08 18:20  llmmkk  阅读(39)  评论(0编辑  收藏  举报