Bzoj4044 Virus synthesis

题意

你要用 \(ATGC\) 四个字母用两种操作拼出给定的串:

  1. 将其中一个字符放在已有串开头或者结尾
  2. 将已有串复制,然后 \(reverse\) ,再接在已有串的头部或者尾部

一开始已有串为空。求最少操作次数。
\(len\le100000\)

Sol

首先有个结论
每次形成偶数长度回文串的最后一步一定是操作 \(2\)
那么考虑一个 \(DP\)
设 \(f[i]\) 表示形成 \(i\) 表示的字符串需要的最少步数
可以去掉首和尾转移来,可以由它的一个前缀或者后缀转移来
如果是个偶数长度的字符串
可以由某个长度小于等于它一半的字符串增长到它的长度后翻倍而来
可以由它去掉首尾的串一步转移而来,因为去掉首位仍然是偶数长度而且形成偶数长度回文串的最后一步一定是操作 \(2\)

那么直接用回文树实现
求某个长度小于等于它一半的字符串直接建树的时候暴力跳一下(雾

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

const int maxn(1e5 + 5);

int f[maxn], son[4][maxn], trans[666], half[maxn], len[maxn], fa[maxn], num[maxn], tot, last, pre[maxn];
char s[maxn];

IL void Init(){
    for(RG int i = 0; i <= tot; ++i){
        len[i] = fa[i] = half[i] = 0;
        for(RG int j = 0; j < 4; ++j) son[j][i] = 0;
    }
    fa[0] = fa[1] = 1, len[1] = -1, tot = 1, last = 0;
}

IL void Extend(RG int pos, RG int c){
    RG int p = last;
    while(s[pos - len[p] - 1] != s[pos]) p = fa[p];
    if(!son[c][p]){
        RG int np = ++tot, q = fa[p];
        while(s[pos - len[q] - 1] != s[pos]) q = fa[q];
        len[np] = len[p] + 2, fa[np] = son[c][q];
        son[c][p] = np, pre[np] = p;
        if(s[pos - len[half[p]] - 1] == s[pos]) half[np] = son[c][half[p]];
        else half[np] = fa[np];
        while((len[half[np]] << 1) > len[np]) half[np] = fa[half[np]];
    }
    last = son[c][p];
}

int main(){
    trans['C'] = 1, trans['G'] = 2, trans['T'] = 3;
    for(RG int t = Input(), n = 0; t; --t){
        Init(), scanf(" %s", s + 1), n = strlen(s + 1);
        for(RG int i = 1; i <= n; ++i) Extend(i, trans[s[i]]);
        RG int ans = n;
        for(RG int i = 2; i <= tot; ++i){
            f[i] = min(len[i], f[fa[i]] + len[i] - len[fa[i]]);
            if(len[i] & 1) f[i] = min(f[pre[i]] + 2, f[i]);
            else{
                f[i] = min(f[i], pre[i] ? f[pre[i]] + 1 : 2);
                f[i] = min(f[i], f[half[i]] + (len[i] >> 1) - len[half[i]] + 1);
            }
            ans = min(ans, f[i] + n - len[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/9153725.html

时间: 2024-10-30 13:03:09

Bzoj4044 Virus synthesis的相关文章

[BZOJ4044]Virus synthesis 回文自动机的DP

4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec  Memory Limit: 128 MB Description Viruses are usually bad for your health. How about fighting them with... other viruses? In this problem, you need to find out how to synthesize such good viruses. W

BZOJ4044 : [Cerc2014] Virus synthesis

设f[x]表示得到x这个回文串的最小步数,则ans=min(n-len[x]+f[x]) 边界条件f[长度为0的偶回文串]=1 因为翻转只会得到偶回文串,所以f[奇回文串]=该串的长度 对于一个偶回文串x,设y为x去掉首尾得到的串,有f[x]=f[y]+1 设y为长度不超过x的一半的x的最长回文后缀,有f[x]=len[x]/2-len[y]+f[y]+1 两种情况取个最小值即可. 对于状态的表示以及最长回文后缀的询问,用回文树支持操作即可.时间复杂度$O(n)$. #include<cstdi

bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp)

bzoj Luogu 你要用ATGC四个字母用两种操作拼出给定的串: 1.将其中一个字符放在已有串开头或者结尾. 2.将已有串复制,然后reverse,再接在已有串的头部或者尾部. 一开始已有串为空.求最少操作次数. len<=100000 题解时间 一个非空串经过一次操作2之后一定是一个偶回文串. 所以考虑建出PAM之后dp. 由于只有偶回文串可以通过操作2产生,所以只有偶回文串参与dp. 对于PAM上一个点 $ x $ : 如果这个串去掉两头一个字母之后的串 $ y $ 非空,那么很明显 $

luogu P4762 [CERC2014]Virus synthesis (回文自动机)

大意: 初始有一个空串, 操作(1)在开头或末尾添加一个字符. 操作(2)在开头或末尾添加该串的逆串. 求得到串$S$所需最少操作数. 显然最后一定是由某个偶回文通过添加字符得到的, 那么只需要求出所有偶回文的最少操作数即可. 结论: 偶回文最后一次进行翻倍操作一定最优. 证明考虑数学归纳, 对于长为$2$的回文串显然成立. 对长度$>2$的偶回文串$A$, 记最后一次翻倍得到的串$B$, $B$的一半为$C$. 记$f(S)$为串$S$的最优值, 就有$f(B)=f(C)+1$. 考虑由$B$

G - Virus synthesis(回文树+dp)

题意 有AGTC四种字符,一开始有一个空串,每次操作,可以在首或尾加任意个字符,或者将已有字符镜面复制(左右两种复制方法),要求最少的操作步数使得得到给出的字符串 Solution: 我们要知道我们要求的是什么,是每一给回文串的所需要的最少的构造操作加上n-这个串的长度 然后就考虑如何求前者 题解:https://blog.csdn.net/nudt_spy/article/details/100061078 Code; #include<bits/stdc++.h> #define N 10

P4762 [CERC2014]Virus synthesis

题意 真是道回文自动机好题. 首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串. 对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为\(x\),我们对于每个\(x\)求出\(trans_x\)表示x的最长后缀回文串,满足\(len_{trans_x}\leqslant len_x/2\). 之后设\(f_x\)表示\(x\)拼成\(x\)这个串的最小代价,我们从\(0\)(偶根)出发进行\(bfs\),中途计算\(f_x\). 对于\(

最近碰到了一个病毒木马:virus.win32.ramnit.B

由于 使用了 简单游 平台上的挂机工具: 番茄-自动人机对战免费版1217  ,使用了很久,头段时间家里电脑 360提示有病毒,本来我一直忽略的,但 我扫描了一下,大量的这个木马,于是 吧 简单游卸载了,全盘扫描,杀毒,修复了许多文件.许多文件被感染,文件大小都变了,还好 360 可以修复感染文件. 这一次,我是用公司电脑,再次 尝试使用 简单游 的挂机工具,没想到 依旧 是这个病毒 virus.win32.ramnit.B ,又是感染了大量的文件.360 居然没有提醒,还好 我清理电脑垃圾 顺

【Paper Reading】Improved Textured Networks: Maximizing quality and diversity in Feed-Forward Stylization and Texture Synthesis

Improved Textured Networks: Maximizing quality and diversity in Feed-Forward Stylization and Texture Synthesis https://arxiv.org/abs/1701.02096v1 本文最主要的贡献有两点: 1. 引入instance normalization 代替 batch normalization 2. 通过使得生产器从Julesz ensemble无偏采样来增加texture

zoj 3430 Detect the Virus(AC自动机)

题目连接:zoj 3430 Detect the Virus 题目大意:给定一个编码完的串,将每一个字符对应着表的数值转换成6位二进制,然后以8为一个数值,重新形成字符 串,判断给定询问串是否含有字符集中的串. 解题思路:主要是题意,逆编码部分注意,转换完了之后,可能有字符'\0',所以不能用字符串的形式储存,要用int型 的数组.注意有相同串的可能. #include <cstdio> #include <cstring> #include <queue> #incl