BZOJ 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

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值