P4555[国家集训队] 最长双回文串 经典manacher题

文章讨论了一种字符串处理问题,即找出给定字符串的最长双回文子串,该子串可以分为两个回文串。通过Manacher算法,可以有效地找到以每个字符为中心的最长回文串,并据此构建双回文串。程序示例展示了如何实现这一算法,包括预处理和计算左右回文子串的边界。
[国家集训队] 最长双回文串
P4555 [国家集训队] 最长双回文串

题目描述

顺序和逆序读起来完全一样的串叫做回文串。比如 acbca 是回文串,而 abc 不是:abc 的顺序为 abc,逆序为 cba,不相同。

输入长度为 n n n 的串 S S S,求 S S S 的最长双回文子串 T T T,即可将 T T T 分为两部分 X , Y X, Y X,Y ∣ X ∣ , ∣ Y ∣ ≥ 1 |X|,|Y|≥1 X,Y1)且 X X X Y Y Y 都是回文串。

输入格式

一行由小写英文字母组成的字符串 S S S

输出格式

一行一个整数,表示最长双回文子串的长度。

样例 #1

样例输入 #1

baacaabbacabb

样例输出 #1

12

提示

样例说明

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

数据范围

对于 100 % 100\% 100% 的数据, 2 ≤ ∣ S ∣ ≤ 1 0 5 2\leq |S|\leq 10^5 2S105

2018.12.10,2018.12.15:感谢 @Ycrpro 提供 hack 数据两组。

分析:这个题有点麻烦,首先有一个比较重要的思维,求一个串由两个回文子串构成,那么我们可以这样想:如果我们知道以s[i]为端点,左边的最长回文子串和右边的最长回文子串,这样两个回文串拼起来就是一个双回文串,所以我们可以试去想有没有办法知道每个以i为端点的左边的和右边的最长回文子串;当然是可以的。

详细可以看这个题解 P4555 【国家集训队最长双回文串】

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int p[N << 1];
char a[N], ma[N << 1];
int l[N << 1], r[N << 1];

void manacher(char s[], int len) {
    int k = 0;
    ma[k++] = '$';
    ma[k++] = '#';
    for (int i = 0; i < len; i++) {
        ma[k++] = s[i];
        ma[k++] = '#';
    }
    // cout << ma << endl;
    int mr = 0, c = 0;
    for (int i = 1; i < k; i++) {
        if (i < mr) {
            p[i] = min(p[(c << 1) - i], p[c] + c - i);
        } else {
            p[i] = 1;
        }
        while (ma[p[i] + i] == ma[i - p[i]])
            p[i]++;
        if (p[i] + i > mr) {
            mr = p[i] + i;
            c = i;
        }
        l[i + p[i] - 1] = max(l[i + p[i] - 1], p[i] - 1);
        r[i - p[i] + 1] = max(r[i - p[i] + 1], p[i] - 1);
    }
}

int main() {
    cin >> a;
    int len = strlen(a);
    manacher(a, len);
    // cout << ' ';
    for (int i = 1; i <= len * 2 + 1; i += 2) {
        r[i] = max(r[i], r[i - 2] - 2);
        // cout << r[i] << ' ';
    }
    // cout << endl;
    // cout << ' ';
    for (int i = len * 2 + 1; i >= 1; i -= 2) {
        l[i] = max(l[i], l[i + 2] - 2);
        // cout << l[i] << ' ';
    }
    // cout << endl;
    int ans = 0;
    for (int i = 1; i < len * 2 + 1; i += 2) {
        if (r[i] && l[i]) {
            ans = max(ans, r[i] + l[i]);
        }
    }
    cout << ans << endl;
}
帮我调试这道的错误代码:目:# P4555 [国家集训队] 最长双回 ## 目描述 顺序和逆序读起来完全一样的叫做回文串。比如 `acbca` 是回文串,而 `abc` 不是:`abc` 的顺序为 `abc`,逆序为 `cba`,不相同。 输入长度为 $n$ 的 $S$,求 $S$ 的最长双回 $T$,即可将 $T$ 分为两部分 $X, Y$($|X|,|Y|≥1$)且 $X$ 和 $Y$ 都是回文串。 ## 输入格式 一行由小写英字母组成的字符 $S$。 ## 输出格式 一行一个整数,表示最长双回的长度。 ## 输入输出样例 #1 ### 输入 #1 ``` baacaabbacabb ``` ### 输出 #1 ``` 12 ``` ## 说明/提示 **样例说明** 从第二个字符开始的字符 `aacaabbacabb` 可分为 `aacaa` 与 `bbacabb` 两部分,且两者都是回文串。 **数据范围** 对于 $100\%$ 的数据,$2\leq |S|\leq 10^5$。 2018.12.10,2018.12.15:感谢 @Ycrpro 提供 hack 数据两组。代码:#include<bits/stdc++.h> using namespace std; char x[100005],s[200005]; int r,c,p[200005],n,ans,k,f[1000005],lazy[1000005]; void build(int l,int r,int k) { lazy[k]=1e9; if(l==r){f[k]=1e9;return; } int mid=(l+r)/2; build(l,mid,2*k); build(mid+1,r,2*k+1); f[k]=min(f[2*k],f[2*k+1]); } void pushdown(int now) { if(lazy[now]==1e9) return; if(f[2*now]) f[2*now]=min(f[2*now],lazy[now]),lazy[2*now]=lazy[now]; if(f[2*now+1]) f[2*now+1]=min(f[2*now+1],lazy[now]),lazy[2*now+1]=lazy[now]; lazy[now]=1e9; } void add(int l,int r,int x,int y,int z,int k) { x=max(1,x);y=min(y,n); if(x<=l&&r<=y){f[k]=min(f[k],z);lazy[k]=min(lazy[k],z);return; } int mid=(l+r)/2; if(x<=mid) add(l,mid,x,y,z,2*k); if(y>mid) add(mid+1,r,x,y,z,2*k+1); f[k]=min(f[2*k],f[2*k+1]); } int ask(int l,int r,int x,int y,int k) { //cout<<l<<" "<<r<<" "<<x<<" "<<y<<" "<<f[k]<<'\n'; x=max(1,x);y=min(y,n); if(x>y) return 1e9; x=max(1,x);y=min(y,n); if(x<=l&&r<=y) return f[k]; int mid=(l+r)/2,ans=1e9; pushdown(k); if(x<=mid) ans=min(ans,ask(l,mid,x,y,2*k)); if(y>mid) ans=min(ans,ask(mid+1,r,x,y,2*k+1)); return ans; } int main() { scanf("%s",x+1);//cin>>x; k=strlen(x+1);s[++n]='#'; for(int i=1;i<=k;i++) s[++n]=x[i],s[++n]='#'; build(1,n,1); for(int i=1;i<=n;i++) { p[i]=((r>=i)?(min(r-i,p[2*c-i])):(1)); while(i-p[i]>=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) p[i]++; if(i>2&&i<n-1) ans=max(ans,i-ask(1,n,i-p[i]+1,i+p[i]-1,1)); if(r<i+p[i]) r=i+p[i],c=i; add(1,n,i+1,i+p[i]-1,i,1); } cout<<ans; return 0; }
最新发布
08-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值