好难啊。。根本不会做。。基本上是抄Claris。。。

题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4044

(luogu)https://www.luogu.org/problemnew/show/P4762

题解: 先观察到三个(ju)性(fei)质(hua): 2操作的结果一定是一个回文串(废话), 最后一个2操作之后只能进行1操作(废话), 只进行1操作花费代价等于字符个数(废话)

三句废话连在一起说: 最后一次2操作产生了一个回文串,此后只能进行1操作,总代价等于回文串代价+目标串长度-回文串长度

又因为一个串的本质不同回文子串个数是\(O(n)\)个,而且奇数长度的回文串没有任何意义,所以实际上有用的状态只是这个串的那些偶数长度的回文子串。。。

然后就可以在回文自动机上DP

考虑如何生成一个偶数长度的回文串,最优方案最后一步肯定是2操作(废话),那么考虑最后2操作之前的一步

因为我们只记录回文串的状态,所以我们希望建立从回文串到回文串的转移,而不能依赖于其他子串。

第一种情况,2操作之前的最后一步将一个字符加在了外面,则代价为该串去掉两头字符后的回文串代价+1 (不需要考虑更多,因为回文串去掉两头还是回文串)

第二种情况,2操作之前的最后一步将字符加在了里面,于是只能找到该串右半侧的最长回文后缀(或左半侧的最长回文前缀)与之建立联系,则代价为(该串串长的一半-右半侧最长回文后缀长度)+右半侧最长回文后缀代价+1 (最后一个+1是指要复制一次,但是第一种情况的+1指的是在复制之前加了一个字符,最后一步复制的代价在去掉两头字符的代价中算过了)

要注意初始值是\(f[0]=1\), 因为要求的是长度为0的回文串的代价,相当于自我复制一遍,以供第一种情况转移。

最后的问题是,如何对于一个串的一个回文子串快速求出不超过其长度一半的最长回文后缀?

建回文自动机,在上面进行递推

一开始觉得是从它的fail递推下来十分直观(因为fail本来就是最长回文后缀),但是实际上这样并不好(可能需要建出“fail树”再在上面做一些树的操作)。

比较好的方法应该是对于当前要递推的\(u\)点找到点\(p\)满足\(son[p][ch]=u\) (\(ch\)是\(u\)的结束字符,\(p\)点在构建回文自动机的过程中已经找到了),然后从\(p\)开始跳fail,直到合法为止,然后再走到它的\(ch\)儿子。因为这样可以避免在fail树上自上而下寻找,而采用更方便的\(son\)来向下寻找。

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;const int N = 1e5+2;
const int S = 4;
char a[N+3];
int len[N+3];
int fail[N+3];
int son[N+3][S+1];
int bd[N+3];
int dp[N+3];
int que[N+3];
int n,siz,lstpos;int decode(char ch)
{if(ch=='A') return 1;else if(ch=='C') return 2;else if(ch=='T') return 3;else if(ch=='G') return 4;
}void initPAM()
{siz = 1; fail[0] = fail[1] = 1; len[0] = 0; len[1] = -1; lstpos = 1; bd[0] = 0; bd[1] = 1;
}void insertchar(int id)
{
//  printf("insert %d\n",a[id]);int p = lstpos;while(a[id-1-len[p]]!=a[id]) {p = fail[p];}if(!son[p][a[id]]){siz++; int u = siz,v = fail[p];while(a[id-1-len[v]]!=a[id]) {v = fail[v];}fail[u] = son[v][a[id]]; len[u] = len[p]+2; son[p][a[id]] = u;
//      printf("p=%d u=%d\n",p,u);if(len[u]<=2) {bd[u] = fail[u];}else{bd[u] = bd[p];while(a[id-1-len[bd[u]]]!=a[id] || (len[bd[u]]+2)*2>len[u]){bd[u] = fail[bd[u]];}bd[u] = son[bd[u]][a[id]];}}lstpos = son[p][a[id]];
}void clear()
{for(int i=0; i<=siz; i++) bd[i] = dp[i] = len[i] = fail[i] = son[i][1] = son[i][2] = son[i][3] = son[i][4] = 0;
}int main()
{int T; scanf("%d",&T);while(T--){initPAM();scanf("%s",a+1); n = strlen(a+1);for(int i=1; i<=n; i++) a[i] = decode(a[i]);for(int i=1; i<=n; i++){insertchar(i);}
//      printf("siz=%d\n",siz);
//      for(int i=0; i<=siz; i++) for(int j=1; j<=S; j++) {if(son[i][j]) printf("son%d %d %d\n",i,j,son[i][j]);}
//      printf("fail: "); for(int i=0; i<=siz; i++) printf("%d ",fail[i]); puts("");
//      printf("bd: "); for(int i=0; i<=siz; i++) printf("%d ",bd[i]); puts("");for(int i=1; i<=siz; i++) {if(len[i]&1) dp[i] = len[i];}int head = 1,tail = 1;que[1] = 0; dp[0] = 1;while(head<=tail){int u = que[head]; head++;for(int i=1; i<=S; i++){if(son[u][i]){tail++; que[tail] = son[u][i];dp[son[u][i]] = min(dp[u]+1,((len[son[u][i]])>>1)-len[bd[son[u][i]]]+dp[bd[son[u][i]]]+1);}}}int ans = n;for(int i=0; i<=siz; i++) ans = min(ans,n-len[i]+dp[i]);printf("%d\n",ans);clear();}return 0;
}

BZOJ4044 Luogu P4762 [CERC2014]Virus Synthesis (回文自动机、DP)相关推荐

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

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

  2. bzoj 4044: [Cerc2014] Virus synthesis 回文树

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

  3. bzoj 4044 Virus synthesis - 回文自动机 - 动态规划

    题目传送门 需要高级权限的传送门 题目大意 要求用两种操作拼出一个长度为$n$的只包含'A','T','G','C'的字符串 在当前字符串头或字符串结尾添加一个字符 将当前字符串复制,将复制的串翻转, ...

  4. 【回文自动机】bzoj3676 [Apio2014]回文串

    回文自动机讲解!http://blog.csdn.net/u013368721/article/details/42100363 pam上每个点代表本质不同的回文子串.len(i)代表长度,cnt(i ...

  5. 算法学习:回文自动机

    [定义] [自动机] 参照AC自动机 [前置知识] [AC自动机] [manacher] 其实不学这两个也可以,但是学过之后会更方便理解 [解决问题] 主要解决回文串的问题 能求出   字符串中回文子 ...

  6. 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities

     Palindromes and Super Abilities Problem's Link:  http://acm.timus.ru/problem.aspx?space=1&num=1 ...

  7. 【知识总结】回文自动机(Palindrome_Automaton)

    参考资料:Palindromic Tree--回文树[处理一类回文串问题的强力工具](请注意,其中似乎有一些错误) 回文自动机似乎和回文树是同一个东西qwq? 回文自动机(PAM)是一种处理回文串的工 ...

  8. 论如何优雅的处理回文串 - 回文自动机详解

    写在前面 最近无意中看到了这个数据结构,顺便也就学习了一下. 而且发现网上关于这个算法的描述有很多地方是错的,在这里做了一些更正. 处理字符串的算法很多: KMP,E-KMP,AC自动机,后缀三兄弟: ...

  9. BZOJ2342[Shoi2011]双倍回文——回文自动机

    题目描述 输入 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. 输出 输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文 ...

最新文章

  1. “性能调优”坑惨了几十万程序员
  2. 三层之抽象工厂加反射实例
  3. requests不加代理
  4. AutoConfigurationImportSelector是什么?
  5. JS求多个数组的重复数据
  6. C语言给出任意4个数算24点,讨论24点算法。
  7. 如何实现开关CD-ROM
  8. Taglist:Exuberant ctags.......
  9. java三次登录锁定_Java基础知识点有哪些 如何快速步入Java行业
  10. sql表达式_SQL表达式
  11. Java基础——JVM内存模型
  12. JAVA代码实现抖音转载视频无水印视频,亲测通过
  13. 固态硬盘用软件测试读不出来,为什么我测不出固态硬盘的官标性能值?
  14. R语言对数据进行非参数检验
  15. DBLE分库分表示例
  16. 2、使用FTP客户端连接FTP服务器
  17. 二叉平衡树之AVL树【手动实现代码】
  18. SpringBoot 实现Excel文件解析
  19. AE开发中“无法嵌入互操作类型*****,请改用适用的接口”解决办法
  20. java 监听客户端的退出_Java socket 服务端如何监控客户端异常关闭?

热门文章

  1. GitHub 新手详细教程转载,亲测可用
  2. 330+ 个机器学习模型/库探索工具!Papers With Code 重磅推出!
  3. java map 查找_Map 查找表操作
  4. ASP.NET开发要抛弃ASP旧习和破烂
  5. 利用WinRAR命令行压缩文件或文件夹2007-11-14 15:07压缩文件夹
  6. 【CyberSecurityLearning 16】单臂路由与DHCP中继+ ICMP协议+VTP
  7. 实验八 分析一个奇怪的程序
  8. 安装虚拟机 Network boot from AMD Operating System no...
  9. stm32 窗口看门狗学习(二)
  10. layui中onchange失效以及form动态渲染失效的问题