2565: 最长双回文串
Description
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
Input
一行由小写英文字母组成的字符串S。
Output
一行一个整数,表示最长双回文子串的长度。
Sample Input
baacaabbacabb
Sample Output
12
【解题报告】
首先可以相当manacher算法当中有这样一个过程, 用两个辅助变量mx和p分别表示已有回文半径覆盖到的最右边界和对应中心的位置, 然后在求解过程中更新mx和p, 考虑到S和T的中间字符作为分隔的位置, 当这个位置被mx覆盖之后, mx从左往右更新第一次覆盖的时候p(覆盖它的串的中心)的位置一定是离这个位置最远的也就是找到了S的中心, 由于mx移动距离至多为n, 所以随着manacher算法的进行用dp来记录mx被更新时的那个回文串的信息即可, 找到S中心之后, 类似的反向从右往左做一遍manacher然后找到T的中心
这样O(n)的预处理之后找出了每个分隔符之下S和T的中心的最佳位置, 最后枚举一下分隔符的位置即可找到最优解
总体时间复杂度O(n)
代码如下:
/**************************************************************
Problem: 2565
User: onepointo
Language: C++
Result: Accepted
Time:80 ms
Memory:14492 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000010
char s1[N],str[N];
int n,len,rl[N],mx,pos,ls[N],rs[N],ans;
int main()
{
scanf("%s",s1);
int i,j,a;
len=strlen(s1);
for(i=0;i<len;i++) str[n++]='*',str[n++]=s1[i];
str[n++]='*';
for(mx=-1,i=0;i<n;i++)
{
if(mx>i) rl[i]=min(mx-i+1,rl[2*pos-i]);
else rl[i]=1;
for(;i+rl[i]<n&&rl[i]<=i&&str[i+rl[i]]==str[i-rl[i]];rl[i]++);
if(i+rl[i]-1>mx) mx=i+rl[i]-1,pos=i;
rs[i-rl[i]+1]=max(rs[i-rl[i]+1],rl[i]-1);
ls[i+rl[i]-1]=max(ls[i+rl[i]-1],rl[i]-1);
}
for(i=0;i<n;i+=2) rs[i]=max(rs[i],rs[i-2]-2);
for(i=n-1;i>=0;i-=2) ls[i]=max(ls[i],ls[i+2]-2);
for(i=0;i<n;i+=2) ans=max(ans,ls[i]+rs[i]);
printf("%d",ans);
return 0;
}