BZOJ 2342: [Shoi2011]双倍回文 [Manacher + set]

题意:

求最长子串使得它有四个相同的回文串SSSS相连组成


 

枚举中间x

找右边的中间y满足 y-r[y]<=x y<=x+r[x]/2

用个set维护

注意中间只能是#

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int n;
char s[N],str[N];
int r[N];
void Manacher(char s[],int n){
    int p=0,a;
    for(int i=1;i<=n;i++){
        r[i]=i<p?min(p-i+1,r[2*a-i]):1;
        while(s[i-r[i]]==s[i+r[i]]) r[i]++;
        if(i+r[i]-1>p) p=i+r[i]-1,a=i;
    }
}
void iniStr(char str[],char s[]){
    for(int i=1;i<=n;i++)
        s[(i<<1)-1]='#',s[i<<1]=str[i];
    s[n<<1|1]='#';
    s[0]='@';s[(n<<1)+2]='$';
}
set<int> Set;
set<int>::iterator it;
struct data{
    int v,id;
    bool operator <(const data a)const{return v<a.v;}
}a[N];
int ans;
void solve(){
    n=n<<1|1; int p=0;
    for(int i=1;i<=n;i++) if(i&1) a[++p].v=i-r[i],a[p].id=i;
    sort(a+1,a+1+p);
    int now=1;
    for(int i=1;i<=n;i++) if(i&1){
        while(now<p&&a[now].v<=i) Set.insert(a[now].id),now++;
        it=--Set.upper_bound(i+r[i]/2);
        if(it!=Set.begin()) ans=max(ans,(*it)-i);
    }
    printf("%d",ans*2);
}
int main(){
    freopen("in","r",stdin);
    scanf("%d%s",&n,str+1);
    iniStr(str,s);
    Manacher(s,n<<1|1);
    solve();
}

 

posted @ 2017-02-13 23:50  Candy?  阅读(237)  评论(0编辑  收藏  举报