【CERC2014】bzoj4044 Virus synthesis

78 篇文章 0 订阅

虽然最终的串不一定是回文串,但是它一定是先生成某个回文串再把剩下的字符加上得到的。因此我们只需要计算每个回文串最少需要几次得到。
如果一个回文串长度为奇数,生成的最后一步一定是添加字符,唯一的生成方法是先生成 fail 再填上剩下的字符。如果是偶数的话,除了上面那种方法,最后一步还可以是翻折。这样也有两种,一种是 SxxS ,一种是 xSSx ,其中 S 表示已有的字符串,x表示新添加的字符。后一种比较好处理,用每个回文串都向它的 trans 指针更新。前一种用每个回文串的 fail 节点中最长的长度不超过它的一半的节点 next 更新。这些都可以按照 val 从小到大的拓扑序更新一遍得到。
需要注意 next 的求法,暴力求 next 复杂度会达到 O(n2) ,但是考虑到一个节点的 fail 节点的 next 一定不会比它的 next 长,我们可以按照上述拓扑序的逆序计算更新 next
这样最后的复杂度是 O(n)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=400010,oo=0x3f3f3f3f;
/*void pause()
{
    int x;
    x=0;
}*/
int id(char c)
{
    switch (c)
    {
        case 'A':return 1;break;
        case 'G':return 2;break;
        case 'C':return 3;break;
        case 'T':return 4;break;
    }
}
char s[maxn];
int a[maxn],trans[maxn][10],fail[maxn],val[maxn],
f[maxn],cnt[maxn],que[maxn],next[maxn];
void solve()
{
    int p,np,q,ans=oo,n,tot=1,last=1;
    scanf("%s",s+1);
    n=strlen(s+1);
    for (int i=1;i<=n;i++) a[i]=id(s[i]);
    for (int i=1;i<=4;i++) trans[0][i]=trans[1][i]=0;
    fail[0]=fail[1]=1;
    val[1]=-1;
    p=1;
    for (int i=1;i<=n;i++)
    {
        p=last;
        while (a[i-val[p]-1]!=a[i]) p=fail[p];
        if (!trans[p][a[i]])
        {
            np=++tot;
            for (int j=1;j<=4;j++) trans[np][j]=0;
            val[np]=val[p]+2;
            q=fail[p];
            while (a[i-val[q]-1]!=a[i]) q=fail[q];
            fail[np]=trans[q][a[i]];
            trans[p][a[i]]=np;
            next[np]=fail[np];
        }
        last=trans[p][a[i]];
    }
    for (int i=0;i<=tot;i++) f[i]=val[i];
    for (int i=0;i<=n;i++) cnt[i]=0;
    for (int i=2;i<=tot;i++) cnt[val[i]]++;
    for (int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
    for (int i=2;i<=tot;i++) que[cnt[val[i]]--]=i;
    /*for (int i=0;i<=tot;i++)
    {
        printf("%d:",i);
        for (int j=1;j<=4;j++) printf("%d ",trans[i][j]);
        putchar('\n');
    }*/
    for (int i=tot-1;i;i--)
    {
        while (val[next[que[i]]]*2>val[que[i]]) next[que[i]]=fail[next[que[i]]];
        if (val[next[que[i]]]<val[next[fail[que[i]]]]) next[fail[que[i]]]=next[que[i]];
    }
    for (int i=1;i<tot;i++)
    {
        //if (i==23) pause();
        //if (que[i]==23) pause();
        //if (que[i]==4) pause();
        f[que[i]]=min(f[que[i]],f[fail[que[i]]]+val[que[i]]-val[fail[que[i]]]);
        if (!(val[que[i]]&1))
        {
            f[que[i]]=min(f[que[i]],f[next[que[i]]]+val[que[i]]/2-val[next[que[i]]]+1);
            for (int j=1;j<=4;j++)
                f[trans[que[i]][j]]=min(f[trans[que[i]][j]],f[que[i]]+1);
        }
        //if (i==23) pause();
        ans=min(ans,n-val[que[i]]+f[que[i]]);
    }
    printf("%d\n",ans);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while (T--) solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值