Lost‘s revenge
Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 4548 Accepted Submission(s): 1274
Problem Description
Lost
and AekdyCoin are friends. They always play "number game"(A boring game
based on number theory) together. We all know that AekdyCoin is the man
called "nuclear weapon of FZU,descendant of Jingrun", because of his
talent in the field of number theory. So Lost had never won the game. He
was so ashamed and angry, but he didn‘t know how to improve his level
of number theory.
One noon, when Lost was lying on the bed, the
Spring Brother poster on the wall(Lost is a believer of Spring Brother)
said hello to him! Spring Brother said, "I‘m Spring Brother, and I saw
AekdyCoin shames you again and again. I can‘t bear my believers were
being bullied. Now, I give you a chance to rearrange your gene sequences
to defeat AekdyCoin!".
It‘s soooo crazy and unbelievable to
rearrange the gene sequences, but Lost has no choice. He knows some
genes called "number theory gene" will affect one "level of number
theory". And two of the same kind of gene in different position in the
gene sequences will affect two "level of number theory", even though
they overlap each other. There is nothing but revenge in his mind. So he
needs you help to calculate the most "level of number theory" after
rearrangement.
Input
There are less than 30 testcases.
For each testcase, first line is number of "number theory gene"
N(1<=N<=50). N=0 denotes the end of the input file.
Next N lines means the "number theory gene", and the length of every "number theory gene" is no more than 10.
The last line is Lost‘s gene sequences, its length is also less or equal 40.
All genes and gene sequences are only contains capital letter ACGT.
Output
For
each testcase, output the case number(start with 1) and the most "level
of number theory" with format like the sample output.
Sample Input
3
AC
CG
GT
CGAT
1
AA
AAA
0
Sample Output
Case 1: 3
Case 2: 2
分析:通过题意,位置可以重置,也就是最后的字符串的AGCT数量和原字符串数量相同.
由题,我们可以想到用AC自动机来记录这些“数论基因”
直观的看的话,用DP来构造,就有41*41*41*41*500种状态,很明显这样是超出内存的
但实际上A+G+C+T<=40 很多状况是不存在的,所以我们就可以进行状态压缩
设 num0=A的数量;num1=G;num2=C;num3=T
这样总共的状态就有(num0+1)*(num1+1)*(num2+1)*(num3+1)*500种,在数量最大的情况下也就是
11*11*11*11*500;
根据进制的特点,我们在列举不重复的情况的时候,可以这样把当前的状态这样化为十进制数
S=A*(num1+1)*(num2+1)*(num3+1)+G*(num2+1)*(num3+1)+C*(num3+1)+T*1;
dp[S][j]也就是当前的状态,j是在自动机上的节点位置
代码如下:
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> using namespace std; #define INF 0x3f3f3f3f int num[4]; int bit[4]; int dp[11*11*11*11+10][550]; int maxx,end_len; struct Trie { int Next[550][4];//4是这里讨论4个小写字母的情况,根据情况修改 int fail[550],end[550];//end数组表示以该节点结尾的字符串的数量 int root,L;//L用来标记节点序号,以广度优先展开的字典树的序号 int newnode() //建立新节点 { for(int i = 0;i < 4;i++) Next[L][i] = -1; //将该节点的后继节点域初始化 end[L++] = 0; return L-1; //返回当前节点编号 } void init() //初始化操作 { L = 0; root = newnode(); } int get_id(char ch) { if(ch==‘A‘)return 0; else if(ch==‘G‘)return 1; else if(ch==‘C‘)return 2; else if(ch==‘T‘)return 3; } void insert(char buf[]) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(Next[now][get_id(buf[i])] == -1) //如果未建立当前的后继节点,建立新的节点 Next[now][get_id(buf[i])] = newnode(); now = Next[now][get_id(buf[i])]; } end[now]++;//以该节点结尾的字符串数量增加1 } void build() { queue<int>Q; //用广度优先的方式,将树层层展开 fail[root] = root; for(int i = 0;i < 4;i++) if(Next[root][i] == -1) Next[root][i] = root; else { fail[Next[root][i]] = root; Q.push(Next[root][i]); } while( !Q.empty() ) { int now = Q.front(); Q.pop(); end[now]+=end[fail[now]]; for(int i = 0;i < 4;i++) if(Next[now][i] == -1) Next[now][i] = Next[fail[now]][i];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配 else { fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点 Q.push(Next[now][i]); } } } void solve() { maxx=0; int maxlen=bit[0]*(num[0]+1); for(int i=0;i<=maxlen+1;i++) for(int j=0;j<L;j++) dp[i][j]=INF; dp[0][0]=0; for(int A=0;A<=num[0];A++) for(int G=0;G<=num[1];G++) for(int C=0;C<=num[2];C++) for(int T=0;T<=num[3];T++) { int s=A*bit[0]+G*bit[1]+C*bit[2]+T*bit[3]; for(int j=0;j<L;j++) { if(dp[s][j]<INF-5) { for(int k=0;k<4;k++) { if(k==0&&A==num[0])continue; else if(k==1&&G==num[1])continue; else if(k==2&&C==num[2])continue; else if(k==3&&T==num[3])continue; if(dp[s+bit[k]][Next[j][k]]==INF) dp[s+bit[k]][Next[j][k]]=dp[s][j]+end[Next[j][k]]; else dp[s+bit[k]][Next[j][k]]=max(dp[s+bit[k]][Next[j][k]],dp[s][j]+end[Next[j][k]]); } } } } int max_len=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3]; for(int i=0;i<L;i++){ if(dp[max_len][i]<INF-5) maxx=max(dp[max_len][i],maxx); } cout<<maxx<<endl; } }; Trie ac; char buf[110]; int main() { int t,n,ans,len,Case=0; while(scanf("%d",&n)!=EOF) { Case++; if(n==0)break; memset(num,0,sizeof(num)); ac.init(); while(n--) { scanf("%s",buf); ac.insert(buf); } ac.build(); scanf("%s",buf); len=strlen(buf); for(int i=0;i<len;i++) { if(buf[i]==‘A‘) num[0]++; else if(buf[i]==‘G‘) num[1]++; else if(buf[i]==‘C‘) num[2]++; else if(buf[i]==‘T‘) num[3]++; } bit[0]=(num[1]+1)*(num[2]+1)*(num[3]+1); bit[1]=(num[2]+1)*(num[3]+1); bit[2]=(num[3]+1); bit[3]=1; printf("Case %d: ",Case); ac.solve(); } return 0; }
HDU 3341 Lost's revenge(AC自动机+状压DP)