JZOJ 4387. 【GDOI2016模拟3.15】基因合成

题目

有T个字符串Si。
一开始,字符串S是空的。
可以进行2中操作:①序列头/尾加字符;②整个字符串S复制一次,反向粘贴在S后面。
问最少经过几次操作,空串变成Si。

题解

对于 | S | <= 200 的数据,记忆化搜索即可。
目前起点是Si,可以任意从头或尾删字符,或对折。
求变成空串所需要的最少步数。

100%?
回文树。涉及到回文的问题,可用回文树解决。
回文树中每个点都是回文串,又涉及步数问题,不妨用DP解决。
怎么找突破口?
实际上,最后得出的Si,都可以看作是操作到某个串,然后进行操作①。
f x 表示得到字符串x所需要的最少步骤。
a n s = m i n ( | S | l e n x + f x )
显然,长度为奇数的回文串,它的答案为字符串长。
考虑字符串x经过谁转移而来。
设x去掉头尾2个字符的串为y,则 f x = f y + 1
设x的长度不超过 l e n x 2 最长回文后缀为z,则 f x = f z + 1 + l e n x 2 l e n z
两种转移取个min即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,ans;
int f[N],qu[N];
int Min(int x,int y){return x<y?x:y;}
struct PAM{
    int pam[N][4],len[N],st[N],fail[N],pre[N],gs,L,wz;
    void clear(){
        memset(st,0,sizeof(st));
        memset(pam,0,sizeof(pam));
        memset(fail,0,sizeof(fail));
        memset(len,0,sizeof(len));
        memset(pre,0,sizeof(pre));
        len[1]=-1;fail[0]=1;st[0]=-1;gs=1;L=0;
    }
    int F(int x){for(;st[L-len[x]-1]!=st[L];x=fail[x]);return x;}
    int Fg(int x,int y){
        if(len[y]<=2)return x;
        for(;len[x]*2>len[y];x=fail[x]);
        return x;
    }
    void Extend(int c){
        st[++L]=c;
        int np=F(wz),nq;
        if(!pam[np][c]){
            len[nq=++gs]=len[np]+2;
            fail[nq]=pam[F(fail[np])][c];
            pam[np][c]=nq;
        }
        wz=pam[np][c];
    }
    int cz(int x){
        if(x==0)return 0;
        if(x==2)return 1;
        if(x==6)return 2;
        return 3;
    }
    void Count(){
        int i,x,y,l=0,r=1;
        memset(pre,-1,sizeof(pre));
        ans=L;
        f[0]=1;
        fo(i,2,gs)if(len[i]&1)f[i]=len[i];
        qu[1]=0;
        while(l<r){
            x=qu[++l];
            fo(i,0,3)if(pam[x][i]){
                y=pam[x][i];
                if(!(~pre[y]))pre[y]=Fg(fail[y],y);
                f[y]=Min(f[x]+1,f[pre[y]]+1+(len[y]>>1)-len[pre[y]]);
                ans=Min(ans,L-len[y]+f[y]);
                qu[++r]=y;
            }
        }
    }
    void Work(){
        char ch=getchar();
        while(ch<'A'||ch>'Z')ch=getchar();
        while(ch>='A'&&ch<='Z')Extend(cz(ch-'A')),ch=getchar();
        Count();
    }
}P;
int main(){
    scanf("%d",&n);
    while(n--){
        P.clear();
        P.Work();
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/81120595