bzoj 4044 Virus synthesis - 回文自动机 - 动态规划
题目传送门
题目大意
要求用两种操作拼出一个长度为$n$的只包含'A','T','G','C'的字符串
- 在当前字符串头或字符串结尾添加一个字符
- 将当前字符串复制,将复制的串翻转,接在当前字符串末尾或者当前字符串头部、
问最少的操作次数。
考虑正常的动态规划时怎么做的。用$f[i][j]$表示拼出给定串的$[l, r]$的最少用的步数。转移显然。
然而它的时间爆了。空间也爆了。
仔细观察发现,翻转完最后一个偶回文串后,两端的字符只能暴力插入。
但是一个串的本质不同的回文串的总数是$O(n)$的级别。因此可以考虑在回文树上动态规划。
然后用$f[x] + n - len(x)$去更新答案。
转移显然要分奇偶性讨论。
- 如果当前的回文串是奇回文串,那么通过它的最长回文后缀转移,然后暴力补上缺字符,即$f[x] = f[fail[x]] + len(x) - len(fail[x])$。
- 如果当前的回文串是偶回文串,那么继续讨论
- 可以通过它的长度不超过它的一半的最长回文后缀补上一半的残缺字符,然后翻转转移。
- 如果通过它去掉两端字符的偶回文串在翻转之前再补上一个字符转移。即$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 - 回文自动机 - 动态规划的更多相关文章
- bzoj 4044: Virus synthesis 回文自动机
题目大意: 你要用ATGC四个字母用两种操作拼出给定的串: 将其中一个字符放在已有串开头或者结尾 将已有串复制,然后reverse,再接在已有串的头部或者尾部 一开始已有串为空.求最少操作次数. le ...
- BZOJ 4044 Virus synthesis (回文自动机+dp)
题目大意: 你可以在一个串的开头或者末尾加入一个字符,或者把当前整个串$reverse$,然后接在前面或者后面,求达到目标串需要的最少操作次数 对目标串建出$PAM$ 定义$dp[x]$表示当前在回文 ...
- bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp)
bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp) bzoj Luogu 你要用ATGC四个字母用两种操作拼出给定的串: 1.将其中一个字符 ...
- BZOJ 4044 Luogu P4762 [CERC2014]Virus Synthesis (回文自动机、DP)
好难啊..根本不会做..基本上是抄Claris... 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4044 (luogu) ...
- [BZOJ4044]Virus synthesis 回文自动机的DP
4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec Memory Limit: 128 MB Description Viruses are us ...
- luogu P4762 [CERC2014]Virus synthesis (回文自动机)
大意: 初始有一个空串, 操作(1)在开头或末尾添加一个字符. 操作(2)在开头或末尾添加该串的逆串. 求得到串$S$所需最少操作数. 显然最后一定是由某个偶回文通过添加字符得到的, 那么只需要求出所 ...
- bzoj 2160: 拉拉队排练 回文自动机
题目: Description 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了.拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛.所以作为拉拉队队长的楚雨荨同学知道,帮助 ...
- Codeforces Gym100543G Virus synthesis 字符串 回文自动机 动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF-100543G.html 题目传送门 - CF-Gym100543G 题意 你可以对一个字符串进行以下两种操 ...
- bzoj 4044: [Cerc2014] Virus synthesis【回文自动机+dp】
建回文自动机,注意到一个回文串是可以通过一个长度小于等于这个串长度的一半的回文串添上一些字符然后复制得到的,也就是在自动机上向fa走,相当于treedp 每次都走显然会T,记录一个up,指向祖先中最下 ...
随机推荐
- MyBatis基础入门《八》查询参数传入Map
MyBatis基础入门<八>查询参数传入Map 描述: 在执行select查询数据的时候,方法传入的参数是java.util.Map类型. 接口方法: xml文件 注意: 书写SQL语句的 ...
- Block 循环引用(中)
不会造成循环引用的block 大部分GCD方法 dispatch_async(dispatch_get_main_queue(), ^{ [self doSomething]; }); 因为self并 ...
- turtle库基础练习
1.画一组同切圆 import turtle turtle.circle(10) turtle.circle(20) turtle.circle(30) turtle.circle(40) turtl ...
- 关于git上的一些错误信息
如果输入$ Git remote add origin git@github.com:djqiang(github帐号名)/gitdemo(项目名).git 提示出错信息:fatal: remote ...
- C语言学习感受
C语言,是我学习的第一种计算机语言,是他作为我编写程序的开始,在学习的时候,先学习了最基础的知识,在语言的理论学习语法上,我逐渐的了解了C语言并且对他有了基础的认识与理解,随着学习内容的不断深入,我逐 ...
- mybatis源码解析5---SqlSession解析
由之前解析可知,mybatis启动的时候会加载XML配置文件解析生成全局配置对象Configuration对象,SqlSessionFactoryBuilder类会根据Configuration对象创 ...
- Chrome浏览器相关细节整理
一.上传文件卡死 可能时由于输入法的原因导致上传文件浏览器卡死.将输入法改为英文模式再操作上传文件就不会卡死了.
- Linux基础命令---join
join 找出两个文件中相同的字段,根据相同字段合并两个文件,将合并结果显示到标准输出. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora ...
- Android获取全局Context的方法
Android获取全局Context的方法 Android--应用全局获取Context - 超宇的博客 - CSDN博客https://blog.csdn.net/chaoyu168/article ...
- javanio1----传统io
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...