• 【BZOJ4044】Virus Synthesis(CERC2014)-回文自动机+DP


    测试地址:Virus Synthesis
    题目大意:一开始你有一个空串,有两种操作:1.往串的前面或后面插入一个A,C,G,T中的字符。2.把整个串翻转后接到这个串前面或后面。问最少需要多少次操作能得到一个给定的字符串?len105
    做法:本题需要用到回文自动机+DP。
    显然一个回文串一定存在一个最优方案,使得最后一次操作是翻转操作。而在最后一次翻转操作后我们得到了一个回文串,最后再手动插入其他字符即可,所以我们只需要算出所有回文子串的最少步数,然后用整个串的长度-回文子串的长度+得到回文子串的最少步数来更新答案即可。
    对于一个奇回文串,它的最后一步一定不可能是翻转,所以答案可以记为它本身的长度。那么接下来我们讨论偶回文串。
    既然我们知道最后一步一定是翻转,那么我们就只要考虑求翻转前的字符串(也就是该回文串的一半)的最少步数即可,这个步数加1就是整个串的答案。
    一种显然的想法是,仿照求最终答案的方法递归计算,然而这个时间复杂度……不说了。于是我们考虑目前情况和求最终答案的情况有哪些不同之处,这个不同之处就在于,我们目前的情况处于一个大回文串中,我们需要进一步发掘相关的性质来加快速度。我们发现,因为我们已知该串在一个回文串内,所以该串的一个前缀的答案,就等于一个更短的回文串的答案1
    上述性质启发我们,按照回文串长度从小到大的顺序进行DP。而考虑到一个回文串时,我们可以在某个串后加上一个字符再翻转得到这个串,这样答案就等于,当前串去掉头尾后得到的回文串的答案加上1。然而我们也可以在一个回文串前插入若干字符再翻转得到这个串,显然这个回文串应该是半串的最长后缀回文串,这样答案就等于这个回文串的答案加上要插入的字符数,再加上1
    那么问题的关键就在于如何找到我们需要的回文串的位置。显然应该建一个回文自动机,这样去掉头尾后的回文串就是它在回文自动机上的父亲,而要求长度小于等于半串长的最长后缀回文串,直接在fail树上跳显然是不行的,会TLE,正确的方法是,因为我们之前已经求了它在回文自动机上父亲的对应位置,那么我们只需要从这个对应位置开始匹配即可,这样复杂度就变成了均摊O(n),于是我们就解决了这一题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int T,n,rt,last,tot;
    int ch[100010][4]={0},pre[100010],fail[100010],len[100010],trans[100010];
    int f[100010],q[100010];
    char s[100010];
    
    int newnode(int l,int fa)
    {
        pre[++tot]=fa;
        len[tot]=l;
        memset(ch[tot],0,sizeof(ch[tot]));
        return tot;
    }
    
    int findfail(int v,int now)
    {
        while(now-len[v]-1<0||s[now-len[v]-1]!=s[now])
            v=fail[v];
        return v;
    }
    
    void extend(int now,int x)
    {
        last=findfail(last,now);
    
        if (!ch[last][x])
        {
            ch[last][x]=newnode(len[last]+2,last);
            if (last==2) fail[tot]=1;
            else fail[tot]=ch[findfail(fail[last],now)][x];
    
            if (len[tot]<=2) trans[tot]=fail[tot];
            else
            {
                int v=trans[last];
                while(s[now-len[v]-1]!=s[now]||((len[v]+2)<<1)>len[tot])
                    v=fail[v];
                trans[tot]=ch[v][x];
            }
        }
        last=ch[last][x];
    }
    
    int change(char c)
    {
        if (c=='A') return 0;
        if (c=='C') return 1;
        if (c=='G') return 2;
        if (c=='T') return 3;
    }
    
    void build()
    {
        tot=last=0;
    
        newnode(0,0),newnode(-1,0);
        fail[1]=2,last=1;
        for(int i=0;i<n;i++)
            extend(i,change(s[i]));
    }
    
    void dfs(int v)
    {
        f[v]=len[v];
        for(int i=0;i<=3;i++)
            if (ch[v][i]) dfs(ch[v][i]);
    }
    
    void dp()
    {
        int h=1,t=1,ans=n;
        q[1]=1;
        while(h<=t)
        {
            int v=q[h++];
            if (v==1) f[v]=1;
            else f[v]=min(f[pre[v]]+1,(len[v]>>1)-len[trans[v]]+f[trans[v]]+1);
            ans=min(ans,n-len[v]+f[v]);
            for(int i=0;i<=3;i++)
                if (ch[v][i]) q[++t]=ch[v][i];
        }
        printf("%d
    ",ans);
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s",s);
            n=strlen(s); 
    
            build();
    
            dfs(2);
            dp();
        }
    
        return 0; 
    }
  • 相关阅读:
    ABP(现代ASP.NET样板开发框架)系列之4、ABP模块系统
    ABP(现代ASP.NET样板开发框架)系列之3、ABP分层架构
    ABP(现代ASP.NET样板开发框架)系列之2、ABP入门教程
    ABP(现代ASP.NET样板开发框架)系列之1、ABP总体介绍
    基于DDD的现代ASP.NET开发框架--ABP系列文章总目录
    参加博客园DDD交流会的情况和感想
    新思想、新技术、新架构——更好更快的开发现代ASP.NET应用程序(续1)
    【python】使用openpyxl解析json并写入excel(xlsx)
    [leetcode]multiply-strings java代码
    线性回归,感知机,逻辑回归(GD,SGD)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793330.html
Copyright © 2020-2023  润新知