bzoj2342 [Shoi2011]雙倍迴文

傳送門
參考題解
一道很好的manacher練手題。。
一開始把題讀錯了,以爲是w1w1w2w2 形式的就可以,題目哪有這麼簡單
觀察題目,我們可以發現:對於每一個符合條件的子串,都有j+p[j]>=i , j < i,p[j]是以j爲中心最大的
畫個圖可能更好理解
這裏寫圖片描述
圖中綠色矩形中就是一種可行方案。
所以manacher計算以每個字符爲中心的迴文半徑,然後枚舉i和j
//據說時限10s暴力能跑過,然而並沒有意義
我們可以用並查集維護當前可行的j,每次往後跳find(j),直到符合要求或者沒有可行方案
原理:如果某一個j已經無法覆蓋當前的i,那麼對於後面的i來說這個j也毫無意義
這樣時間複雜度就非常優美了

CODE:

#include<cstdio>
char a[1000010];
int p[1000010];
int f[1000010];
int n,len,ans,pos,mx;
inline int max(const int &a,const int &b){return a>b?a:b;}
inline int min(const int &a,const int &b){return a<b?a:b;}
int find(int n)
{
    if(f[n]!=n) f[n]=find(f[n]);
    return f[n];
}
int main()
{
    scanf("%d\n",&n);
    a[0]='$';
    for(int i=1;i<=n;i++)
      a[++len]='#',a[++len]=getchar();
    a[len+1]=a[len+2]='#';
    for(int i=1;i<=len;i++)
    {
        if(i<mx) p[i]=min(p[pos*2-i],mx-i);
        else p[i]=1;
        while(a[i-p[i]]==a[i+p[i]]) p[i]++;
        if(i+p[i]>mx) mx=i+p[i],pos=i;
    }
    for(int i=1;i<=len;i++)
      if(a[i]=='#') f[i]=i;
      else f[i]=i+1;
    for(int i=3;i<=len;i+=2)
    {
        int tmp=i-(p[i]>>1);
        if(!tmp) continue;
        tmp=find(tmp);
        while(tmp+p[tmp]<i&&tmp<i) f[tmp]=find(tmp+1),tmp=f[tmp];
        if(tmp<i) ans=max(ans,(i-tmp)<<1);
    }
    printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章