题解:P8131 [ICPC 2020 WF] Gene Folding

· · 题解

题目大意

寻找头尾偶数回文子串,将其折叠,然后组成一个新的字符串,一直到没有一个偶数回文子串时输出剩下的长度。

思路分析

代码思路

如果一个个去寻找回文子串,然后折叠组成新字符串的时间复杂度太高了,那么我们有更高效的解法。

我们可以从首尾依次寻找每个回文子串半径,更新左右端点。

但是可能会有多个重叠偶数回文子串怎办呢?

此时我们需要判断这个回文子串范围有没有超过左右端点。

注意左端点不能超过右端点,否则会重复删除。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=4e6+10;
int sum[MAXN<<1],n,nt;
char s[MAXN<<1],c[MAXN];
void init(){
    int k=0;
    s[k++]='$'; //开头特殊符号 (判断边界) 
    s[k++]='@';
    for (int i=0;i<n;i++){
        s[k++]=c[i];
        s[k++]='@';
    }
    s[k++]='^';//结尾特殊符号 (判断边界)
    nt=k;
}
void manacher(){
    int mr=0,mid=0;
    for(int i=1;i<nt;i++){
        if(i<mr) sum[i]=min(sum[2*mid-i],mr-i);
        else sum[i]=1;
        while(s[i+sum[i]]==s[i-sum[i]])     sum[i]++;
        if(i+sum[i]>mr){
            mr=i+sum[i];
            mid=i;
        }
    }
}

int main (){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>c;
    n=strlen(c);
    init();
    manacher();
    int l=1,r=nt-2;                     //  因为预处理字符串开头是'$',结尾是'^',所以有效部分从1到nt-2
    for (int i=2;i<nt-1;i++){           //  处理左端点 
        if (s[i]=='@'&&i-sum[i]+1<= l){
            l=i;
        }
    }
    for (int i=nt-2;i>l;i--){       //处理右端点 注意i>l防止r<l 
        if (s[i]=='@'&&i+sum[i]-1>=r){  
            r=i;
        }
    }
    cout<<(r-l)/2;
    return 0;
}