您的位置:首页 > 其它

BZOJ 2565 最长双回文串

2017-11-24 17:24 190 查看
Descri
4000
ption


顺序和逆序读起来完全一样的串叫做回文串。比如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

HINT

样例说明

从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。

对于100%的数据,2≤|S|≤105。

2015.4.25新加数据一组

Source

2012国家集训队Round 1 day2

思路

首先maracher跑一遍,然后分别更新一个点i不包含它的左侧的最长回文子串和右侧的最长回文字串的长度(记为li和ri),就拿这个串来说吧:abbab,扩充后得到#b#a#b#b#a#b#这个串,那么第6个(倒数第2个)#号左侧的最长回文字串就是abba,右侧的最长回文字串就是b,注意是原串的长度。

那么计算li时,由于选择串需要尽量选择最长的,就是串的中心越小越好,因此可以从左到右扫一遍,把还没有计算的都计算了,下次计算时又从还没有计算的第一个位置开始计算,这样能够确保时间复杂度为O(n),计算右侧时同理。

还有些细节在代码中讲。

代码

#include <cstdio>
#include <cstring>

const int maxn=100000;

char s[maxn+10],a[(maxn<<1)+10];
int p[(maxn<<1)+10],l[(maxn<<1)+10],r[(maxn<<1)+10],id,rmax,len,ans;

int main()
{
scanf("%s",s+1);
len=strlen(s+1);
a[0]='!';
a[1]='$';
for(register int i=1; i<=len; ++i)
{
a[i<<1]=s[i];
a[i<<1|1]='$';
}
len=len<<1|1;
a[len+1]='*';//预处理
p[1]=id=rmax=1;
for(register int i=2; i<=len; ++i)//maracher
{
if(i>rmax)
{
p[i]=1;
}
else
{
if(rmax-i>p[(id<<1)-i])
{
p[i]=p[(id<<1)-i];
}
else
{
p[i]=rmax-i;
}
}
while(a[i+p[i]]==a[i-p[i]])
{
++p[i];
}
if(i+p[i]-1>rmax)
{
rmax=i+p[i]-1;
id=i;
}
}
int now=0;//now记录最后一个更新的是哪一个位置
for(register int i=1; i<=len; ++i)
{
for(register int j=now+1; j<=i+p[i]; ++j)
{
l[j]=j-i;//从上一个没有更新的位置开始,一直计算到不能用i位置更新为止
}
if(i+p[i]+1>now)
{
now=i+p[i];//更新now值
}
}
//上一次扫描完成后,now一定为n
for(register int i=len; i>=1; --i)
{
for(register int j=now-1; j>=i-p[i]; --j)
{
r[j]=i-j;//这个i-j和上面的j-i可以推出来,因为不考虑预处理时插入的字符'$'
}
if(i-p[i]-1<now)
{
now=i-p[i];
}
}
for(register int i=1; i<=len; i+=2)//只计算预处理时插入的字符
{
if(l[i]+r[i]>ans)
{
ans=l[i]+r[i];
}
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: