hdoj5340(Manacher+枚举)

题目链接:https://vjudge.net/problem/HDU-5340

题意:给定一个长度为n的字符串,问能不能将其分解成3个回文串。(n<=2e4)

思路:

  先用Manacher算法得到以每个点为中心的最大回文串的左右边界le[i]、ri[i]。并且当le[i]==1(即该回文串向左可以到达字符串的边界)时,记录下来,le1[ri[i]]=1,表示存在一个回文串左边界为1,右边界为ri[i]。同理,当ri[i]==n-1(字符串右边界)时,ri1[le[i]]=1。

  然后遍历中间的回文串的回文中心,令t1=p[i]-1(回文串长度),t2=le[i](回文串左边界),t3=ri[i](回文串右边界),当le1[t2]==1&&ri1[t3]==1时即找到了方案。如果不满足,t1-=2,t2+=2,t3-=2,继续判断,直到t1<=0。要注意的是当t2==1或者t3==n-1时该回文串不能作为中间回文串。

  详见代码。

AC code:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn=2e4+5;
int T,n,ans,p[maxn<<1],le[maxn<<1],ri[maxn<<1];
int le1[maxn<<1],ri1[maxn<<1];
char s[maxn<<1],ss[maxn];

void manacher(){
    int mid=0,r=0;
    for(int i=1;i<n;++i){
        if(r>=i) p[i]=min(p[(mid<<1)-i],r-i+1);
        while(s[i-p[i]]==s[i+p[i]]) ++p[i];
        if(i+p[i]>r) r=i+p[i]-1,mid=i;
        int tmp=p[i]-1;
        le[i]=i-tmp,ri[i]=i+tmp;
        if(le[i]==1) le1[ri[i]]=1;
        if(ri[i]==n-1) ri1[le[i]]=1;
    }
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%s",ss);
        ans=0;
        n=strlen(ss);
        s[0]='~',s[1]='|';
        for(int i=0;i<n;++i)
            s[2*i+2]=ss[i],s[2*i+3]='|';
        n=2*n+2;
        for(int i=0;i<n;++i)
            p[i]=0,le1[i]=0,ri1[i]=0;
        manacher();
        for(int i=1;i<n;++i){
            int t1=p[i]-1,t2=le[i],t3=ri[i];
            if(t2==1||t3==n-1){
                t1-=2;
                t2+=2;
                t3-=2;
            }
            while(t1>0){
                if(le1[t2]&&ri1[t3]){
                    ans=1;
                    break;
                }
                t1-=2;
                t2+=2;
                t3-=2;
            }
            if(ans) break;
        }
        if(ans) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/FrankChen831X/p/12418993.html