[CERC2014]Virus synthesis

[CERC2014]Virus synthesis

题意:

初始有一个空串,有以下操作:

  1. 串开头或末尾添加一个字符
  2. 向前复制,形成回文串

询问要操作成给定的串需要多少次操作。

分析:

首先,我们还是先求出来对应给定的串的后缀自动机。

可以大致推算出来,这种题目一般都是 \(DP\) ,我们设 \(dp[i]\) 表示转移到 \(i\) 节点代表的回文串的最少需要次数。

因为我们尽量想让操作变少,因此肯定操作二越多越好。

因此最后的答案肯定是由一个回文串+暴力添加 的来,答案为 \(min(dp[i]+n-len[i])\)

对于一个串 \(i\) ,如果前后加一个字母形成 \(j\), 则 \(dp[j]=dp[i]+1\)

我们通过 \(trans\) 找到最长的 \(x\) 后缀回文串 \(x\) 满足 \(len[x] \leq len[i]/2\)

那么则有 \(dp[i]=min(dp[i],dp[x]+1+len[i]/2-len[x])\) ,即暴力后复制。

绿色部分即为我们要添加的部分,因此就为上面公式

然后用队列记录状态,保证转移到有序的。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T;
int fail[N],trans[N],n,val[N];
int son[N][5],last,len[N],cnt,ans;
char s[N];

int dp[N],que[N];

int new_node(int x){
    len[++cnt]=x;
    memset(son[cnt],0,sizeof(son[cnt])*5); 
    return cnt;
}

int getfail(int x,int now){
    while(s[now-len[x]-1]!=s[now]) x=fail[x]; return x;
}

void init(){
    memset(son[0],0,sizeof(int)*5); memset(son[1],0,sizeof(int)*5);//直接清理,会炸第一个点
    val['A']=0, val['T']=1,val['C']=2, val['G']=3;
    s[0]=-1,last=0,cnt=1;fail[0]=1,len[1]=-1;
}

void build_PAM(){
    for(int i=1;i<=n;i++){
        int now=getfail(last,i),x=val[s[i]];
        // cout<<now<<endl;
        if(!son[now][x]){
            int newnode=new_node(len[now]+2);
            fail[newnode]=son[getfail(fail[now],i)][x];
            son[now][x]=newnode;
            if(len[newnode]<=2) trans[newnode]=fail[newnode];
            else{
                int tmp=trans[now];
                while(s[i-len[tmp]-1]!=s[i]||((len[tmp]+2)<<1)>len[newnode]) tmp=fail[tmp];
                trans[newnode]=son[tmp][x];
            }
        }
        last=son[now][x];
    }
}

int main(){
    cin>>T;
    while(T--){
        scanf("%s",s+1);  init(); 
        ans=n=strlen(s+1);
        build_PAM();
        for(int i=2;i<=cnt;i++) dp[i]=len[i];
        int L=1,R=0; que[++R]=0, dp[0]=1;
        while(L<=R){
            int now=que[L++];
            for(int i=0;i<4;i++){
                int x=son[now][i],y=trans[x]; 
                if(!x) continue;
                dp[x]=dp[now]+1;
                dp[x]=min(dp[x],dp[y]+1+len[x]/2-len[y]);
                ans=min(ans,dp[x]+n-len[x]);
                que[++R]=x;
            }
        }
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

posted @ 2021-09-25 18:37  Evitagen  阅读(31)  评论(0编辑  收藏  举报