基因合成

题目描述

这里写图片描述

回文自动机

首先,我们可以认为,每个最终串都是进行若干操作后进行了一次第二个操作后,再在两端插入而得到的。
先对最终串插入回文自动机上,然后做动态规划。
我们设f[i]表示i这个结点所代表的回文串最后一次操作是第二个操作需要多少个操作得到。
注意偶数树的根节点f值为1。
fa[i]表示其在回文树上的父亲,pre[i]表示其最长长度不超过其的长度的一半的回文后缀。pre怎么求呢?显然沿着fail一直跳跳到第一个长度符合要求的即可。
然而这样找pre的复杂度是玄学的(是的能过但是我们思索如果是一堆a呢?),所以其实正解估计是倍增。
如果结点i是奇回文串,f[i]=len[i]。
否则f[i]=min(f[fa[i]]+1,f[pre[i]]+len[i]/2-len[pre[i]]+1)
答案怎么求?
枚举回文树上每个结点,然后ans=min(ans,n-len[i]+f[i])

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int f[maxn],g[maxn][5],fail[maxn],pre[maxn],fa[maxn],len[maxn];
int i,j,k,l,t,n,m,tot,ca,ans,last;
char ch;
char s[maxn];
void insert(int c){
    int x=last;
    while (s[i-len[x]-1]!=s[i]) x=fail[x];
    if (!g[x][c]){
        fa[++tot]=x;
        len[tot]=len[x]+2;
        int y=fail[x];
        while (s[i-len[y]-1]!=s[i]) y=fail[y];
        fail[tot]=g[y][c];
        y=g[y][c];
        while (len[y]>len[tot]/2) y=fail[y];
        pre[tot]=y;
        if (len[tot]%2==0) f[tot]=min(f[fa[tot]]+1,f[pre[tot]]+len[tot]/2-len[pre[tot]]+1);
        else f[tot]=len[tot];
        g[x][c]=tot;
    }
    last=g[x][c];
}
int main(){
    //freopen("dna4.in","r",stdin);
    scanf("%d",&ca);
    while (ca--){
        scanf("%s",s+1);
        n=strlen(s+1);
        ans=n;
        fail[0]=fail[1]=tot=1;
        len[1]=-1;
        f[1]=2;
        f[0]=1;
        fo(i,0,n) 
            fo(j,0,3)
                g[i][j]=0;
        last=0;
        fo(i,1,n){
            if (s[i]=='A') t=0;
            else if (s[i]=='T') t=1;
            else if (s[i]=='C') t=2;else t=3;
            insert(t);
        }
        fo(i,2,tot) ans=min(ans,n-len[i]+f[i]);
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值