[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.

We have prepared for you a set of strings of the letters A, G, T and C. They correspond to the

DNA nucleotide sequences of viruses that we want to svnthesize, using the following operations:

* Adding a nucleotide either to the beginning or the end of the existing sequence

* Replicating the sequence, reversing the copied piece, and gluing it either to the beginmng or

to the end of the original (so that e.g., AGTC can become AGTCCTGA or CTGAAGTC).

We‘re concerned about efficiency, since we have very many such sequences, some of them verv

long. Find a wav to svnthesize them in a mmimum number of operations.

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

1.将其中一个字符放在已有串开头或者结尾

2.将已有串复制,然后reverse,再接在已有串的头部或者尾部

一开始已有串为空。求最少操作次数。

len<=100000

Input

The first line of input contains the number of test cases T. The descriptions of the test cases

follow:

Each test case consists of a single line containing a non-empty string. The string uses only

the capital letters A, C, G and T and is not longer than 100 000 characters.

Output

For each test case, output a single line containing the minimum total number of operations

necessary to construct the given sequence.

Sample Input

4
AAAA
AGCTTGCA
AAGGGGAAGGGGAA
AAACAGTCCTGACAAAAAAAAAAAAC

Sample Output

3
8
6
18

题解:

这道题是一道回文自动机的DP好题啊。。。很难想,解释起来似乎也很混乱,

有不明白欢迎询问,因为这样写题解也不能完全解释明白233

我们考虑,假设答案为ans,那初始化ans=串长.

如果ans能够减小,那一定是某一个回文串通过复制来做出的贡献.

注意,只能是一个,因为一旦复制就要复制全串,

最终的目标串一定是添加字符(可以是0个,即不添加)形成的,而不可能有两段复制.

由于这是一个回文有关题目......manacher看起来还不能搞

所以我们只好先把回文自动机建出来,对于回文自动机上每个节点i设f[i]表示生成节点i代表的串所需要的最少操作次数

那么这个串的生成可以是在对称轴外侧填字符+复制,也可以是在对称轴内侧填字符+复制

如果是在外侧:

假设串i可以在串j复制之前在外侧添加一个字符+复制得到,

那么我们可以想到,f[i]=min(f[j]+1)

或者,f[i]可以通过某个回文后缀对称轴内侧填字符+复制得到,

那么这个回文后缀的长度一定小于len[i]/2

回文后缀我们可以通过暴力跳fail指针来寻找

转移方程为f[i]=min(len[i]/2+f[j]-len[j]+1)

但是对于本题的数据范围这样会T掉……

那么我们考虑,对于i的某个可行复制串回文字串j,以及fail[j]

f[j]-len[j]的值显然要大于f[fail[j]]-len[fail[j]],那么我们只需要考虑第一个合法决策点,比它长的不会更优秀

但是这样还是会T……

所以我们考虑对于某个节点i,我们可以记录它的第一个最优决策点。

那么它的最优决策点可以从它的fail的最优决策点开始选择

(由于i和fail[i]的查找路径是一样的,len[fail]比len[i]长度更短,那么对于fail合法对于i也会合法)

所以在寻找i的决策点时,我们从fail[i]的最优决策点开始查找即可。

这样构建回文自动机的时候处理决策点,再遍历一遍求出每个串的f值,最后答案就是ans=min(n-len[i]+f[i])

代码实现:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N=100010,inf=0x7fffffff;
 6 char s[N];
 7 struct Pam_Tree
 8 {
 9     int n,m,last,p,strategy[N];
10     int ch[N][4],len[N],fail[N],f[N];
11     int q[N],hd,tl;
12     inline int id(char x)
13     {
14         switch(x)
15         {
16             case ‘A‘:return 0;
17             case ‘C‘:return 1;
18             case ‘G‘:return 2;
19             case ‘T‘:return 3;
20         }
21         return 0;
22     }
23     inline int newnode(int l)
24     {
25         len[p]=l;memset(ch[p],0,sizeof(ch[p]));
26         fail[p]=f[p]=strategy[p]=0;
27         return p++;
28     }
29     inline int getfail(int x)
30     {
31         while(s[n-len[x]-1]!=s[n])x=fail[x];
32         return x;
33     }
34     inline void work()
35     {
36         register int i,x,u;
37         hd=1,tl=0,q[++tl]=0,f[0]=1;
38         int ans=m;
39         for(i=2;i<p;++i)if(len[i]&1)f[i]=i;
40         while(hd<=tl)
41             for(x=q[hd++],i=0;i<4;++i)
42                 if((u=ch[x][i]))
43                     q[++tl]=u,
44                     f[u]=min(f[x]+1,len[u]/2+f[strategy[u]]-len[strategy[u]]+1),
45                     ans=min(ans,f[u]+m-len[u]);
46         printf("%d\n",ans);
47     }
48     inline void insert()
49     {
50         register int i,now,cur,d,x;
51         for(n=1;n<=m;++n)
52         {
53             d=id(s[n]),cur=getfail(last);
54             if(!ch[cur][d])
55             {
56                 now=newnode(len[cur]+2),
57                 fail[now]=ch[getfail(fail[cur])][d],
58                 ch[cur][d]=now;
59                 if(len[now]<=2)strategy[now]=fail[now];
60                 else
61                 {
62                     x=strategy[cur];
63                     while(s[n-len[x]-1]!=s[n]||(len[x]+2)*2>len[now])x=fail[x];
64                     strategy[now]=ch[x][d];
65                 }
66             }
67             last=ch[cur][d];
68         }
69         work();
70     }
71     inline void intn()
72     {
73         scanf("%s",s+1),p=0,newnode(0),newnode(-1);
74         s[0]=1,m=strlen(s+1),last=0,fail[0]=1,insert();
75     }
76 }PT;
77 int main()
78 {
79     int t;scanf("%d",&t);
80     while(t--)PT.intn();
81 }
时间: 2024-10-10 15:47:18

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

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$

[加强版] Codeforces 835D Palindromic characteristics (回文自动机、DP)

题目链接: https://codeforces.com/contest/835/problem/D 题意: 一个回文串是\(1\)-回文的,如果一个回文串的左半部分和右半部分一样且都是\(k\)-回文串(右半部分是指长度为该串长度除以二下取整的后缀),则该串为\((k+1)\)回文串,满足该串是\(k\)回文串的最大\(k\)称作该串的回文级别.给定一个串\(s\), 对于每一个\(k=1,2,...,n\)求出该串中有多少个位置不同的\(k\)-回文串.\(n\le 5\times 10^6

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

回文自动机讲解!http://blog.csdn.net/u013368721/article/details/42100363 pam上每个点代表本质不同的回文子串.len(i)代表长度,cnt(i)代表个数(要最后在fail树上dp一遍方可). 答案直接枚举一遍结点,然后用len(i)*cnt(i),取最大者即可. 回文自动机是非常优越的数据结构,可惜比manacher多一个字符集的空间-- #include<cstdio> #include<cstring> #include

回文树或者回文自动机,及相关例题

回文树简述 在大部分说法中,回文树与回文自动机指的是一个东西: 回文树是对一个字符串,基于自动机思想构建的处理回文问题的树形结构: 回文树是对着一个单串建立的: 于是他主要用于计数(回文子串种类及个数) 基本建立思路是先建立其前缀的回文树,然后每加上一个字符,统计影响: 回文树存在fail指针但一般不承接字符串匹配问题: (回文树大概可以判定一个回文串是不是一个串的子串,但KMP之类的可以做得更好) 构建好的回文树,是这样的: (好难看) 可看出: 存在两个树结构,分别记录奇数|偶数长度的回文:

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

题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最大出现值. 输入 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. 输出 输出一个整数,为逝查回文子串的最大出现值. 样例输入 [样例输入l] abacaba [样例输入2] www 样例输出 [样例输出l] 7 [样例输出2] 4 题解 回文自动机裸题 关于PAM个人暂时理解不是很深入,挖坑待填. 本题只需要统计fail树的子树大小,再

回文串区间dp

UVa 10739 String to Palindrome(经典回文串区间DP) 题意: 给定一个字符串,可以对其进行删除,插入,替换操作. 问最少经过几次操作,可以使这个字符串变成回文字符串. 思路: 看得别人的 题解,最优化问题,用较为直接的方法处理时发现情况很复杂,很多时候就要考虑动态规划了.先从整体出发,由大到小,看往少一个两个元素的情况进行最优递归,如何得到结果. (这里区间DP即是不断向两侧扩大规模) (1)如果最外层两个字符相同,s[0]==s[n],那么这两个字符外侧肯定不用考

UVa 10617 Again Palindrome(回文串区间DP)

UVa 10617 Again Palindrome(经典回文串区间DP) 题意: 给定一个字符串s,对s进行删除操作,使得剩下的子串是回文字符串,问最多有多少种这种子串. 思路: 涉及到回文字符串,首先要想到的肯定是区间DP,如何写出状态转移方程? 直接从题意切入:dp[i, j]表示区间[i, j]最多有多少个这样的子串. 1. s[i] == s[j] 去掉s[i],则一个子问题就是dp[i+1, j]; 去掉s[j],另一个子问题就是dp[i, j-1]; 显然这两个子问题是会有重叠的,

编程之美资格赛 第二题 回文字符序列 dp

这是一道dp题,设置dp[i][j]代表的是从i到j之间的有多少个回文子串,dp[i][j] = dp[i][num[1]] +1+ dp[num[1]+1][j - 1]+1......+dp[num[j]][j-1] + 1 ,num[i] 代表的是与i字符相同的上一个字符的位置! 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定字符串,求它的回文子序列个数.回文子序列反转字符顺序后仍然与原序列相同.例如字符串aba中,回文子序列为"a", &quo