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

 

posted @ 2017-09-24 21:17  LadyLex  阅读(266)  评论(0编辑  收藏  举报