BZOJ——2565最长双回文串

本文介绍了一种求解最长双回文子串的问题,通过马拉车算法填充字符串并求出每个字符的回文半径,进而找到符合条件的最长双回文子串及其长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接->传送门

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|≤10^5



思路:马拉车填充后求出每个字符的回文半径,在枚举所有的#,找以此#为中心最左和最右的回文串长度,这里分别用l[i]和r[i]表示,i是小回文串的中心,双回文串长度恰好等于l[i]+r[i]。

贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#define N 100005
using namespace std;
int p[N<<1],l[N<<1],r[N<<1];
char s[N],t[N<<1];
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    int i;
    for(i=1; i<=len; i++)
        t[2*i]=s[i-1],t[2*i+1]='#';
    t[0]='!',t[1]='#';
    t[2*len+2]='\0';
    len=2*len+1;
    int mx=0,id=0,ans=0;
    for(i=1; i<=len; i++)
    {
        if(mx>i) p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while(t[i+p[i]]==t[i-p[i]]) p[i]++;
        if(mx<i+p[i]) mx=i+p[i],id=i;
    }
    i=1;
    for(id=3; id<=len-2; id+=2)//#为分割点枚举
    {
        for(; i<=len; i++)//i不用从头开始循环,否则超时
        {
            if(i+p[i]-1>=id)//小回文串回文半径大于枚举中心才行
            {
                l[id]=i;//以id为结尾的最左边字符串中心为i
                break;//跳出,分割点右移,因为如果再让i增加就不是最左的回文串了
            }
        }
    }
    i=len;
    for(id=len-2; id>=3; id-=2)
    {
        for(; i>=1; i--)
        {
            if(i-(p[i]-1)<=id)
            {
                r[id]=i;
                break;
            }
        }
    }
    for(id=3; id<=len-2; id++)
        ans=max(ans,r[id]-l[id]);
    printf("%d\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值