BZOJ-2565 最长双回文串(Manacher算法)
题目描述
顺序和逆序读起来完全一样的串叫做回文串。比如 acbca 是回文串,而 abc 不是(abc 的顺序为 abc,逆序为 cba,不相同)。
输入长度为 \(n(1\leq n\leq 10^5)\) 的串 \(s\),求 \(s\) 的最长双回文子串 \(t\),即可将 \(t\) 分为两部分 \(x,y(1\leq |x|,|y|)\),且 \(x\) 和 \(y\) 都是回文串。
分析
在 \(\text{Manacher}\) 算法中,以位置 \(i\) 为回文中心的最长回文半径为 \(p[i]-1\),在处理 \(p[i]\) 时,同时记录两个信息:\(l[i]\) 表示以位置 \(i\) 为 结尾 的最长回文子串的长度,则 \(l[i+p[i]-1]=\max(l[i+p[i]-1],p[i]-1)\);\(r[i]\) 表示以位置 \(i\) 为 开头 的最长回文子串的长度,则 \(r[i-p[i]+1]=\max(r[i-p[i]+1],p[i]-1)\)。
预处理之后,递推以每个 # 为断点的 \(l[i]\) 和 \(r[i]\):\(l[i]\) 是以位置 \(i\) 为结尾的最长回文子串长度,逆推,\(l[i]=\max(l[i],l[i+2]-2)\);\(r[i]\) 是以位置 \(i\) 为开头的最长回文子串长度,顺推,\(r[i]=\max(r[i],r[i-2]-2)\)。
最后枚举每个 # 为断点,更新最大值即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N],s[N];
int len,p[N],l[N],r[N];
void Manacher()
{
len=strlen(a);
s[0]='$';s[1]='#';
for(int i=0;i<len;i++)
{
s[2*i+2]=a[i];
s[2*i+3]='#';
}
int maxright=0,mid=0;
for(int i=0;i<=2*len+1;i++)
{
if(i<maxright)
p[i]=min(p[mid*2-i],maxright-i);
else
p[i]=1;
while(s[i-p[i]]==s[i+p[i]])
p[i]++;
if(i+p[i]>maxright)
{
maxright=i+p[i];
mid=i;
}
l[i+p[i]-1]=max(l[i+p[i]-1],p[i]-1);
r[i-p[i]+1]=max(r[i-p[i]+1],p[i]-1);
}
}
int main()
{
scanf("%s",a);
Manacher();
for(int i=1;i<=2*len+1;i=i+2)
r[i]=max(r[i],r[i-2]-2);
for(int i=2*len+1;i>=1;i=i-2)
l[i]=max(l[i],l[i+2]-2);
int ans=-1;
for(int i=1;i<=2*len+1;i=i+2)
if(l[i]!=0&&r[i]!=0)
ans=max(ans,l[i]+r[i]);
cout<<ans<<endl;
return 0;
}
posted on 2020-11-20 21:14 DestinHistoire 阅读(95) 评论(0) 收藏 举报
浙公网安备 33010602011771号