洛谷 P4555 [国家集训队]最长双回文串 解题报告
P4555 [国家集训队]最长双回文串
题目描述
顺序和逆序读起来完全一样的串叫做回文串。比如acbca
是回文串,而abc
不是(abc
的顺序为abc
,逆序为cba
,不相同)。
输入长度为\(n\)的串\(S\),求\(S\)的最长双回文子串\(T\),即可将\(T\)分为两部分\(X\),\(Y\),(\(|X|,|Y|≥1\))且\(X\)和\(Y\)都是回文串。
输入输出格式
输入格式:
一行由小写英文字母组成的字符串\(S\)。
输出格式:
一行一个整数,表示最长双回文子串的长度。
说明
对于\(100\%\)的数据,\(2≤|S|≤10^5\)
按套路先把每个位置为中心的回文串长度用\(manacher\)求出来。
然后比较暴力的想法是直接线段树做一下,虽然也可以通过不过不优雅。
预处理出每个点为开头或结尾的最长回文串长度,然后枚举断点就可以了。
如何预处理?发现可以先通过中心的最长回文串预处理一部分,然后扫描一遍,更新非最长的长度。
具体的说,比如\(pre_i\)代表\(i\)开头的最长回文串,那么显然是有\(pre_i=max(pre_{i-2}-2,pre_i)\)的(这里已经插入了'#')
这个技巧用的比较多了,要注意使用。
Code:
#include <cstdio>
#include <cstring>
const int N=2e5+10;
int a[N],t[N],L[N],R[N],n,ans,r;
char s[N],c[N];
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
int main()
{
memset(t,0x3f,sizeof(t));
scanf("%s",c+1);
n=strlen(c+1);
s[0]=s[1]='$';
for(int i=1;i<=n;i++) s[i*2]=c[i],s[i*2+1]='$';
n=(n<<1)+1;
for(int rig=0,mid,i=1;i<=n;i++)
{
a[i]=i>rig?1:min(rig-i,a[(mid<<1)-i]);
while(s[i-a[i]]==s[i+a[i]]) ++a[i];
--a[i];
if(a[i]+i>rig) rig=a[i]+i,mid=i;
L[i-a[i]]=max(L[i-a[i]],a[i]);
R[i+a[i]]=max(R[i+a[i]],a[i]);
}
for(int i=3;i<=n;i+=2) L[i]=max(L[i],L[i-2]-2);
for(int i=n;i>0;i-=2) R[i]=max(R[i],R[i+2]-2);
for(int i=1;i<=n;i+=2) ans=max(ans,L[i]+R[i]);
printf("%d\n",ans);
return 0;
}
2018.10.31