题目传送门

  需要高级权限的传送门

题目大意

  要求用两种操作拼出一个长度为$n$的只包含'A','T','G','C'的字符串

  1. 在当前字符串头或字符串结尾添加一个字符
  2. 将当前字符串复制,将复制的串翻转,接在当前字符串末尾或者当前字符串头部、

  问最少的操作次数。

  考虑正常的动态规划时怎么做的。用$f[i][j]$表示拼出给定串的$[l, r]$的最少用的步数。转移显然。

  然而它的时间爆了。空间也爆了。

  仔细观察发现,翻转完最后一个偶回文串后,两端的字符只能暴力插入。

  但是一个串的本质不同的回文串的总数是$O(n)$的级别。因此可以考虑在回文树上动态规划。

  然后用$f[x] + n - len(x)$去更新答案。

  转移显然要分奇偶性讨论。

  1. 如果当前的回文串是奇回文串,那么通过它的最长回文后缀转移,然后暴力补上缺字符,即$f[x] = f[fail[x]] + len(x) - len(fail[x])$。
  2. 如果当前的回文串是偶回文串,那么继续讨论
    • 可以通过它的长度不超过它的一半的最长回文后缀补上一半的残缺字符,然后翻转转移。
    • 如果通过它去掉两端字符的偶回文串在翻转之前再补上一个字符转移。即$f[y] + 1$

  另外说一句,奇回文串的$f[x]$设为$len(x)$也没有任何影响。

  求$half$的做法可以参考NOI某年的动物园。

Code

 /**
* bzoj
* Problem#4044
* Accepted
* Time: 3264ms
* Memory: 4512k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; int cti(char x) {
if (x == 'A') return ;
if (x == 'G') return ;
if (x == 'T') return ;
return ;
} const int N = 1e5 + ; typedef class TrieNode {
public:
int len, f;
TrieNode* ch[];
TrieNode *fail, *half;
}TrieNode; TrieNode pool[N];
TrieNode* top; TrieNode* newnode(int len) {
top->len = len;
memset(top->ch, , sizeof(top->ch));
return top++;
} typedef class PalindromeTree {
public:
char *str;
TrieNode *even, *odd;
TrieNode *last; PalindromeTree() { }
PalindromeTree(char *str):str(str) {
top = pool;
odd = newnode(-);
even = newnode();
odd->fail = odd, even->fail = odd, last = odd;
odd->half = odd, even->half = odd;
} TrieNode* extend(TrieNode* p, int pos) {
while (str[pos] != str[pos - p->len - ]) p = p->fail;
return p;
} void build() {
for (int i = ; str[i]; i++) {
int c = cti(str[i]);
last = extend(last, i);
if (!last->ch[c]) {
TrieNode* pn = newnode(last->len + );
pn->fail = extend(last->fail, i)->ch[c];
if (!pn->fail)
pn->fail = even;
TrieNode* f = last->half;
f = extend(f, i)->ch[c];
if (!f)
f = even;
while (f->len * > pn->len)
f = f->fail;
pn->half = f;
last->ch[c] = pn;
}
last = last->ch[c];
}
} void dp() {
for (TrieNode* p = pool; p < top; p++)
p->f = p->len;
for (TrieNode* p = pool + ; p < top; p++)
if (p->len & )
p->f = p->fail->f + (p->len - p->fail->len);
else {
p->f = min(p->f, p->half->f + ((p->len >> ) - p->half->len) + );
for (int c = ; c < ; c++)
if (p->ch[c])
p->ch[c]->f = min(p->ch[c]->f, p->f + );
}
}
}PalindromeTree; int T;
char str[N];
PalindromeTree pam; inline void init() {
scanf("%s", str + );
} inline void solve() {
pam = PalindromeTree(str);
pam.build();
pam.dp();
int res = , len = strlen(str + );
// for (TrieNode* p = pool; p < top; p++)
// cerr << p - pool << " " << p->fail - pool << " " << p->half - pool << " " << p->len << endl;
for (TrieNode* p = pool; p < top; p++)
res = min(res, len - p->len + p->f);
printf("%d\n", res);
} int main() {
scanf("%d", &T);
while (T--) {
init();
solve();
}
return ;
}

bzoj 4044 Virus synthesis - 回文自动机 - 动态规划的更多相关文章

  1. bzoj 4044: Virus synthesis 回文自动机

    题目大意: 你要用ATGC四个字母用两种操作拼出给定的串: 将其中一个字符放在已有串开头或者结尾 将已有串复制,然后reverse,再接在已有串的头部或者尾部 一开始已有串为空.求最少操作次数. le ...

  2. BZOJ 4044 Virus synthesis (回文自动机+dp)

    题目大意: 你可以在一个串的开头或者末尾加入一个字符,或者把当前整个串$reverse$,然后接在前面或者后面,求达到目标串需要的最少操作次数 对目标串建出$PAM$ 定义$dp[x]$表示当前在回文 ...

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

    bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp) bzoj Luogu 你要用ATGC四个字母用两种操作拼出给定的串: 1.将其中一个字符 ...

  4. BZOJ 4044 Luogu P4762 [CERC2014]Virus Synthesis (回文自动机、DP)

    好难啊..根本不会做..基本上是抄Claris... 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4044 (luogu) ...

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

    4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec  Memory Limit: 128 MB Description Viruses are us ...

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

    大意: 初始有一个空串, 操作(1)在开头或末尾添加一个字符. 操作(2)在开头或末尾添加该串的逆串. 求得到串$S$所需最少操作数. 显然最后一定是由某个偶回文通过添加字符得到的, 那么只需要求出所 ...

  7. bzoj 2160: 拉拉队排练 回文自动机

    题目: Description 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了.拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛.所以作为拉拉队队长的楚雨荨同学知道,帮助 ...

  8. Codeforces Gym100543G Virus synthesis 字符串 回文自动机 动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF-100543G.html 题目传送门 - CF-Gym100543G 题意 你可以对一个字符串进行以下两种操 ...

  9. bzoj 4044: [Cerc2014] Virus synthesis【回文自动机+dp】

    建回文自动机,注意到一个回文串是可以通过一个长度小于等于这个串长度的一半的回文串添上一些字符然后复制得到的,也就是在自动机上向fa走,相当于treedp 每次都走显然会T,记录一个up,指向祖先中最下 ...

随机推荐

  1. cocos 简便斗地主发牌动画

    niuniuSend : function (int) { switch(int) { case 0: for(var i=0;i<5;i++){ var sp = new ccui.Image ...

  2. mysql 命令一套

    MySQL mysql -h主机地址 -u用户名 -p用户密码 首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root  -p,回车后提示你输密码.注意用户名前可以有 ...

  3. VB脚本错误,系统找不到制定的文件 。代码:80070002

    希望得到网友的答案:非常感谢!

  4. iframe中父页面与子页面的传值方法

    涉及到iframe传值的情况有这么几种:(1)父页面给iframe中的子页面传值(2)子页面调用父页面中的函数(3)iframe中的子页面给父页面传值(4)兄弟iframe之间的传值 下面来逐一看一下 ...

  5. RDD、DataFrame、Dataset三者三者之间转换

    转化: RDD.DataFrame.Dataset三者有许多共性,有各自适用的场景常常需要在三者之间转换 DataFrame/Dataset转RDD: 这个转换很简单 val rdd1=testDF. ...

  6. c++ 各种类型转换

    1.int 2 string 法1:c++11里面的to_string #include <string> std::); //or auto s = std::to_string(); ...

  7. 使用js调用js

    直接上源码: <div class="choose"> choose a mode<br> <hr> <button type=" ...

  8. C# SQLite 数据库操作

    C# SQLite 数据库操作学习 运行环境:Window7 64bit,.NetFramework4.61,C# 7.0 参考: SQLite 官网 SQL As Understood By SQL ...

  9. Chess (SG + 状态压缩预处理)

    #include<bits/stdc++.h> #define bit(t) (1 << t) using namespace std; <<; ;//k是集合s的 ...

  10. STL容器vector应用注意事项

    [1]提前分配足够空间以免不必要的重新分配和复制代价 关于vector容器重新分配和复制及析构释放的代价,请参见随笔<STL容器之vector>. 应用示例对比代码如下: #include ...