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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章