P12213 [蓝桥杯 2023 国 Python B] 最长回文前后缀(Manacher)

P12213 [蓝桥杯 2023 国 Python B] 最长回文前后缀(Manacher)

题目传送门

前言

前置知识:Manacher 求回文串

题意简介

给定一个字符串 \(S\),求取其前后缀拼接后能形成的最大回文串长度。

思路

由于我们并不想对后缀进行操作,故将 \(S\) 翻转为 \(S'\),答案显然由两部分构成:

  • \(S\)\(S'\) 的最长公共前缀(将 \(S'\) 翻转回去这将分别作为答案的左右两端)。

  • \(S\)\(S'\) 分别求接在最长公共前缀后的最长回文串,取其较大者(直接作为答案的中间部分)。

求解方法

第一部分的答案可以暴力枚举计算,对于第二部分,Manacher 可以求解 \(arr_i\),即以位置 \(i\) 为中心的最大回文串半径,此时 \(i-arr_i+1\) 就是该回文串的左端点,若该左端点刚好是第一部分所求出前缀的下一个位置则可以用于更新第二部分的答案。

时间复杂度 \(O(n)\)

Code

#include<iostream>
#include<algorithm>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=1e5+5;
string str1,str2;
int Calc(string tmp)
{
    int n=tmp.length(),L=0,R=-1,arr[N<<2]={0},maxx=0;
    char S[N<<2];
    tmp=" "+tmp,S[0]='&';
    for(int i=1;i<=n;i++)
        S[(i<<1)-1]=tmp[i],S[i<<1]='&';
    for(int i=0;i<=(n<<1);i++)
    {
        if(i<=R) arr[i]=min(arr[L+R-i],R-i);
        while(i-arr[i]>=0&&i+arr[i]<=(n<<1)&&S[i-arr[i]]==S[i+arr[i]]) arr[i]++;
        if(i+arr[i]-1>R) L=i-arr[i]+1,R=i+arr[i]-1;
    }
    //上面是Manacher的板子
    for(int i=0;i<=(n<<1);i++)
        if(i-arr[i]+1==0) maxx=max(maxx,arr[i]-1);
    return maxx;
}
int main()
{
    IOS;
    cin>>str1;
    str2=str1;
    reverse(str2.begin(),str2.end());//翻转字符串
    int pre=-1;//求解最长公共前缀
    while(pre+1<str1.length()&&str1[pre+1]==str2[pre+1]) pre++;
    //通过substr截取最长公共前缀的下一位至末端,这一段才是第二部分答案的可用部分
    cout<<((pre+1)<<1)+max(Calc(str1.substr(pre+1,str1.length()-pre-1)),Calc(str2.substr(pre+1,str2.length()-pre-1)))<<'\n';
    return 0;
}

完结撒花~

posted @ 2025-08-07 14:24  FallingGardenia  阅读(9)  评论(0)    收藏  举报