Codeforces 1326D2 - Prefix-Suffix Palindrome (Hard version)

题目大意

T组数据,每组给定一个字符串 s

求一个最长的字符串 t ,满足:

  1. t 是一个回文串
  2. t = a+b ,a是字符串s的前缀,b是字符串s的后缀,‘+‘ 为拼接两字符串,ab可能为空串

数据范围

数据组数不超过 1e5

字符串的总共长度不超过 1e6

解题思路

(标准做法应该是哈希)

因为对于任意的字符串T,设R(T)为T的倒置

T+回文串+R(T) 仍然是一个回文串

可以直接双指针在s里找出最长的 T和R(T)

while(L<R&&s[L]==s[R])
    L++,R--;

(上面这步O(n)不能省略)

但是因为数据范围内的字符数量很大,所以不能继续采用Easy版的O(n^2)暴力法

很容易想到,对于中间剩下的子串,要想求出与两边相邻的最长回文串

就可以采用Manacher算法来 O(n) 处理加计算

在初始化函数里传入两个变量 l 和 r ,表示只要处理 [ l , r ) 区间内的字符即可

void initStr(int l,int r){
    int k=0;
    str[k++]='@';
    for(int i=l;i<=r;i++){
        str[k++]='#';
        str[k++]=s[i];
    }
    str[k++]='#';
    len=k;
    str[k]='\0';
}

在Manacher处理过程中,计算出一个Len值就可以判断一次是不是与左或者与右相邻

void manacher(){
    int mx=0,id=0;
    for(int i=1;i<len;i++){
        if(mx>i)
            Len[i]=min(mx-i,Len[2*id-i]);
        else
            Len[i]=1;
        while(str[i+Len[i]]==str[i-Len[i]])
            Len[i]++;
        if(Len[i]+i>mx){
            mx=Len[i]+i;
            id=i;
        }

        if(i==Len[i])//说明此时这个回文串左相邻
            if(TP<Len[i])
                TP=Len[i]-1,GR=1;
        if(i+Len[i]==len)//说明此时这个回文串右相邻
            if(TP<Len[i])
                TP=Len[i]-1,GR=2;
    }
}

上面的代码中,TP表示最长回文串长度,GR为1表示左相邻,2表示右相邻

最后输出左+中+右即可

完整代码

(77ms / 2000ms)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+50;

string s;
char str[MAXN*2];
int Len[MAXN*2],len,TP,GR;

void initStr(int l,int r){
    int k=0;
    str[k++]='@';
    for(int i=l;i<=r;i++){
        str[k++]='#';
        str[k++]=s[i];
    }
    str[k++]='#';
    len=k;
    str[k]='\0';
}

void manacher(){
    int mx=0,id=0;
    for(int i=1;i<len;i++){
        if(mx>i)
            Len[i]=min(mx-i,Len[2*id-i]);
        else
            Len[i]=1;
        while(str[i+Len[i]]==str[i-Len[i]])
            Len[i]++;
        if(Len[i]+i>mx){
            mx=Len[i]+i;
            id=i;
        }

        if(i==Len[i])
            if(TP<Len[i])
                TP=Len[i]-1,GR=1;
        if(i+Len[i]==len)
            if(TP<Len[i])
                TP=Len[i]-1,GR=2;
    }
}

void solve()
{
    cin>>s;
    int LEN=s.size();

    int L=0,R=LEN-1;
    while(L<R&&s[L]==s[R])
        L++,R--;

    if(L>=R)
    {
        cout<<s<<'\n';
        return;
    }

    initStr(L,R);
    TP=0;
    manacher();
    for(int i=0;i<L;i++)
        cout<<s[i];
    if(GR==1)
    {
        for(int i=L,j=0;j<TP;i++,j++)
            cout<<s[i];
    }
    else
    {
        for(int i=R-TP+1;i<=R;i++)
            cout<<s[i];
    }
    for(int i=R+1;i<LEN;i++)
        cout<<s[i];

    cout<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

原文地址:https://www.cnblogs.com/stelayuri/p/12529117.html

时间: 2024-07-30 14:09:48

Codeforces 1326D2 - Prefix-Suffix Palindrome (Hard version)的相关文章

Codeforces 1335E2 - Three Blocks Palindrome (hard version)

题面 题意/解题思路 直接延用 Easy 版本的想法即可 详解见上一篇博客Codeforces 1335E1 - Three Blocks Palindrome (easy version) 完整程序 (93ms/2000ms) #include<bits/stdc++.h> using namespace std; int ar[200050]; vector<int> v[210]; void solve() { int n,ans=0; cin>>n; for(i

Codeforces 1335E1 - Three Blocks Palindrome (easy version)

题面 题意 给定一个长度为 n 的数列 定义要求的回文子数列满足下图条件 其中 x 与 y 可以为 0 即这个回文子数列可以是数字完全相同的一个子数列 也可以是只包含两种数字,且其中一种平均分布在另一种数字的两侧 求出最长的回文子数列长度 解题思路 在输入时往vector里记录下每个数字出现的位置 然后开始枚举位于两侧的数字的种类 i( i = 1 ~ 26 ) 首先考虑这个回文子数列只包含一种数字,刚好根据枚举 直接将答案与枚举的字符数量取大(与枚举到的vector[i].size取大) 然后

Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version) -- manacher

D2. Prefix-Suffix Palindrome (Hard version) 题目链接 manacher做法 #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int p[N*2]; string manacher(string ss) { int len = ss.size(); string s; s.resize(len*2+2); for(int i=len;i>=0;i--) {

Codeforces - 1203D2 - Remove the Substring (hard version) - 双指针

https://codeforces.com/contest/1203/problem/D2 上次学了双指针求两个字符串之间的是否t是s的子序列.但其实这个双指针可以求出的是s的前i个位置中匹配t的最长的前缀.反过来求一次可以得到最长的后缀. 然后怎么找要删除的位置呢?暴力n^2肯定可以,然后线性写挂到自闭. 枚举位置[i,j),注意j可以取相等,所以预处理前后缀的时候把n位置的后缀也算好. 去除子串[i,j),那么剩下的就是[0,i-1]和[j,n-1]两个子串,他们匹配的长度加起来超过tl就

CodeForces 837F - Prefix Sums | Educational Codeforces Round 26

按tutorial打的我血崩,死活挂第四组- - 思路来自FXXL /* CodeForces 837F - Prefix Sums [ 二分,组合数 ] | Educational Codeforces Round 26 题意: 设定数组 y = f(x) 使得 y[i] = sum(x[j]) (0 <= j < i) 求初始数组 A0 经过多少次 f(x) 后 会有一个元素 大于 k 分析: 考虑 A0 = {1, 0, 0, 0} A1 = {1, 1, 1, 1} -> {C(

mybatis之&lt;trim prefix=&quot;&quot; suffix=&quot;&quot; suffixOverrides=&quot;&quot; prefixOverrides=&quot;&quot;&gt;&lt;/trim&gt;的含义

转自:http://blog.csdn.net/qq_33054511/article/details/70490046 <trim prefix="" suffix="" suffixOverrides="" prefixOverrides=""></trim> prefix:在trim标签内sql语句加上前缀. suffix:在trim标签内sql语句加上后缀. suffixOverrides:指定

Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version)

Link 题意: \(a\) 是 \(s\) 的前缀,\(b\) 是 \(s\) 的后缀 使 \(a+b\) 是可以找到的最大的回文串 \(a\) 或 \(b\) 可以是空串 思路: 找出最大长度 \(k\) 使 \(s[0,k-1]+s[len(s)-k,len(s)-1]\) 是回文串 再用 Manacher 算法求出 \(s[k,len(s)-k-1]\) 中以 \(s[k]\) 为右端的的最大回文串和以 \(s[len(s)-k-1]\)为左端的最大回文串(计算出 \(len[i]\)

Codeforces Gym 100570 E. Palindrome Query Manacher

E. Palindrome QueryTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100570/problem/E Description De Prezer loves palindrome strings. A string s1s2...sn is palindrome if and only if it is equal to its reverse. De Prezer also love

Codeforces 1108E2 Array and Segments (Hard version) 差分, 暴力

Codeforces 1108E2 E2. Array and Segments (Hard version) Description: The only difference between easy and hard versions is a number of elements in the array. You are given an array \(a\) consisting of \(n\) integers. The value of the \(i\)-th element