Codeforces 1326D2 - Prefix-Suffix Palindrome 马拉车

博客介绍了如何利用马拉车算法解决Codeforces 1326D2问题,即寻找字符串中最长的前缀后缀回文串。博主分享了四种情况的分析和代码实现,强调了边界条件的处理,并提到比赛中遇到的挑战和代码的美观性问题。

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

几个月没写马拉车了,比赛的时候调了半天,代码也写的极丑


题意:

给你字符串s,让你找到一个最长的回文串a+b,字符串a为s的前缀,b为s的后缀

题解:

先对s做一遍马拉车

细分有四种情况:

1.b为空,即最长的回文串就是某前缀,代码中该长度记为le

2.a为空,即最长的回文串就是某后缀,代码中该长度记为ri

3.a>=b,a的前b个字符与b对应为回文串,a串后(a-b)为回文串。代码中old=min(a,b),tmp=max(a,b)-min(a,b)

4.a<b,b的后a个字符与a对应为回文串,b串前(b-a)为回文串。代码中old=min(a,b),tmp=max(a,b)-min(a,b)

注意下细节,边界情况的考虑。为了简单计算,在马拉车过后的Mp[]数组上进行分析

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000050;
char Ma[maxn*2];
int Mp[maxn*2];
int Manacher(char s[],int len)
{
    int l = 0;
    Ma[l++] = '$';
    Ma[l++] = '#';
    for(int i = 0; i<len; i++)
    {
        Ma[l++] = s[i];
        Ma[l++] = '#';
    }
    Ma[l] = 0;
    int mx = 0, id = 0;
    for(int i = 0; i<l; i++)
    {
        Mp[i] = mx>i ? min(Mp[2*id-i],mx-i) : 1;
        while(i-Mp[i]>=0 && Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
    return l;
}

char s[maxn];
int p[maxn];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%s",s);
        int slen = strlen(s);
        if(slen==1){
            printf("%s\n",s);
            continue;
        }
        int len = Manacher(s,slen),old=0;
        int le=0,ri=0,l=0,r=slen-1,n=slen*2;
        for(int i = 2; i<len; i++){
            if(i-Mp[i]+1<=2){
                le=max(le,i-1);
            }
            if(i+Mp[i]-1>=n){
                ri=max(ri,Mp[i]-1);
            }
        }
        while(l<r){
            if(s[l]==s[r]){
                old++;
                l++,r--;
                if(l>=r){
                    printf("%s",s);
                    break;
                }
            }
            else{
                int tmp=0,flag=0;
                l=2*(l+1);r=2*(r+1);
                for(int i = 2; i<len; i++){
                    int len=Mp[i]/2+1;
                    int L=i-Mp[i]+1;
                    int R=i+Mp[i]-1;
                    if(L>R) continue;
                    if(L<=l&&i-l<=r-i&&(i-l+1)>tmp){
                        tmp=(i-l+1);
                        flag=0;
                    }
                    if(R>=r&&i-l>=r-i&&(r-i+1)>tmp){
                        tmp=(r-i+1);
                        flag=1;
                    }
                }
                if(tmp+old*2>max(le,ri)){
                    if(flag){
                        for(int i=0;i<old;i++) printf("%c",s[i]);
                        for(int i=slen-tmp-old;i<slen;i++) printf("%c",s[i]);
                    }
                    else{
                        for(int i=0;i<old+tmp;i++) printf("%c",s[i]);
                        for(int i=slen-old;i<slen;i++) printf("%c",s[i]);
                    }
                }
                else{
                    if(le>ri) for(int i=0;i<le;i++) printf("%c",s[i]);
                    else for(int i=slen-ri;i<slen;i++) printf("%c",s[i]);
                }
                break;
            }
        }
        printf("\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值