hdu 3341 Lost's revenge(dp+Ac自动机)
题目:revenge
Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 3369 Accepted Submission(s): 896
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
CG
AT
1
AA
AAA
0
Sample Output
Case 1: 3
Case 2: 2
做这题真的是闹心,只怪自己开始没有估计好时间复杂度瞎折腾,走了不少弯路。第一次想用Ac自动机检验长串的各种排列取最大值即可,但是长串有可能存在字符想通过的情况,排列一定小于length!,怎样才能把所有的非重复的字符串排列找出来呢?《C语言名题精选百则》里有个旋转法,但是我研究了半天也没有把重复的去掉。后来发现next_permutation是个好东西,可以除去重复的串,但前提是初始串是单调的,这简单,我用一个sort不就可以了吗?开心啊。。
TLE:
#include #include #include #include using namespace std;int ans;char word[15],mystring[50];struct node{ int count; node *next[4],*fail; node(){ count=0; fail=NULL; memset(next,0,sizeof(next)); }};node *root;int getdex(char a){ int ans; if(a=='A')ans=0; else if(a=='C') ans=1; else if(a=='G') ans=2; else ans=3; return ans;}void insert(char *s){ int length=strlen(s); node *p=root; for(int i=0;inext[dex]==0)p->next[dex]=new node(); p=p->next[dex]; } p->count++;}node *que[1000];void build_ac(){ int head,tail; head=tail=0; que[tail++]=root; while(headnext[i]){ if(p==root)p->next[i]->fail=root; else { temp=p->fail; while(temp){ if(temp->next[i]){ p->next[i]->fail=temp->next[i]; break; } temp=temp->fail; } if(!temp)p->next[i]->fail=root; } que[tail++]=p->next[i]; } } }}void del(node *p){ if(p==NULL)return ; for(int i=0;i<4;i++)del(p->next[i]); delete p;}int query(int str[],int str_len){ int i,dex,result=0; node *p=root; for(i=0;inext[str[i]]==NULL&&p!=root)p=p->fail; p=p->next[str[i]]; if(!p)p=root; node *temp=p; while(temp!=root){ //&&temp->count!=-1 result+=temp->count; //temp->count=-1; temp=temp->fail; } } return result;}void show(int str[],int len){ for(int i=0;i>n&&n){ root=new node(); while(n--){ scanf("%s",word); insert(word); } build_ac(); scanf("%s",mystring); int str_len=strlen(mystring); int str[50]; for(int i=0;i郁闷啊,TLE了,是不是指针太耗时间了,于是我又把指针改成数组:
TLE:
#include #include #include #include #include using namespace std;int ans,top;char word[15],mystring[50];struct node{ int next[4],fail; int count; node(){ count=0; fail=NULL; memset(next,0,sizeof(next)); }}tree[510];int getdex(char a){ int ans; if(a=='A')ans=0; else if(a=='C') ans=1; else if(a=='G') ans=2; else ans=3; return ans;}void insert(char *s){ int length=strlen(s),p=0; for(int i=0;i q; q.push(0); int i,p,cur,son; while(!q.empty()){ p=q.front(); q.pop(); for(int i=0;i<4;i++){ if(tree[p].next[i]){ son=tree[p].next[i]; if(p==0)tree[son].fail=p; else { cur=tree[p].fail; while(cur&&tree[cur].next[i]==0)cur=tree[cur].fail; tree[son].fail=tree[cur].next[i]; } if(tree[tree[son].fail].count) tree[son].count+=tree[tree[son].fail].count; q.push(son); } else tree[p].next[i]=tree[tree[p].fail].next[i]; } }}int query(int str[],int str_len){ int i,dex,result=0; int p=0; for(i=0;inext[str[i]]; if(!p)p=0; int temp=p; while(temp!=0){ //&&temp->count!=-1 result+=tree[temp].count; //temp->count=-1; temp=tree[temp].fail; } } return result;}void show(int str[],int len){ for(int i=0;i>n&&n){ //tree[0].node(); while(n--){ scanf("%s",word); insert(word); } build_ac(); scanf("%s",mystring); int str_len=strlen(mystring); int str[50]; for(int i=0;i还是TLE!能不能愉快的玩耍了?仔细分析:原来40长度4种最小单位的长串排列估算有:40!/10!/10!/10!/10!=4.71e21,哎,一开始怎么不想想这个问题,还做了半天无用功!接下来怎么办呢?看了大神们的思路,DP啊,空间换时间。组合情况:11*11*11*11<15000。真是巧妙啊。。
#include #include #include #include #include using namespace std;int ans,top;char word[15],mystring[50];struct node{ int next[4],fail; int count;}tree[510];int getdex(char a){ int ans; if(a=='A')ans=0; else if(a=='C') ans=1; else if(a=='G') ans=2; else ans=3; return ans;}void insert(char *s){ int length=strlen(s),p=0; for(int i=0;i q; q.push(0); int i,p,cur,son; while(!q.empty()){ p=q.front(); q.pop(); for(int i=0;i<4;i++){ if(tree[p].next[i]){ son=tree[p].next[i]; if(p==0)tree[son].fail=p; else { cur=tree[p].fail; while(cur&&tree[cur].next[i]==0)cur=tree[cur].fail; tree[son].fail=tree[cur].next[i]; } if(tree[tree[son].fail].count) tree[son].count+=tree[tree[son].fail].count; q.push(son); } else tree[p].next[i]=tree[tree[p].fail].next[i]; } }}int myhash[41][41][41][41];int ta,tc,tg,tt;int dp[15000][505];void mydp(char *S){ ta=tc=tg=tt=0; int i,j,k,l,num=0; for(i=0;S[i]!=0;i++) if(S[i]=='A') ta++; else if(S[i]=='C') tc++; else if(S[i]=='G') tg++; else tt++; for(i=0;i<=ta;i++) for(j=0;j<=tc;j++) for(k=0;k<=tg;k++) for(l=0;l<=tt;l++) myhash[i][j][k][l]=num++;}void solve(int ca){ int i,j,k,l,p,q,son,x1,x2,mmax=0; dp[0][0]=0; for(i=0;i<=ta;i++) for(j=0;j<=tc;j++) for(k=0;k<=tg;k++) for(l=0;l<=tt;l++) { if(i+j+k+l==0) continue; x1=myhash[i][j][k][l]; for(p=0;p<=top;p++) { for(q=0;q<4;q++) { if(q==0&&i-1>=0) x2=myhash[i-1][j][k][l]; else if(q==1&&j-1>=0) x2=myhash[i][j-1][k][l]; else if(q==2&&k-1>=0) x2=myhash[i][j][k-1][l]; else if(q==3&&l-1>=0) x2=myhash[i][j][k][l-1]; else continue; if(dp[x2][p]==-1) continue; son=tree[p].next[q]; dp[x1][son]=max(dp[x1][son],dp[x2][p]+tree[son].count); if(dp[x1][son]>mmax) mmax=dp[x1][son]; } } } printf("Case %d: ",ca); printf("%d\n",mmax);}int main(){ //freopen("cin.txt","r",stdin); int n,ca=1; while(cin>>n&&n){ top=0; memset(dp,-1,sizeof(dp)); while(n--){ scanf("%s",word); insert(word); } build_ac(); scanf("%s",mystring); mydp(mystring); solve(ca++); memset(tree,0,sizeof(tree)); } return 0;}
build_ac()内和普通的失败指针建立函数相比最重要的两句(//后面的两句):
if(tree[tree[son].fail].count) ; //tree[son].count+=tree[tree[son].fail].count; q.push(son);}else ; //tree[p].next[i]=tree[tree[p].fail].next[i];
直接在建立失败指针时就更新各个节点的count。后面的状态转移过程直接使用。
普通的build_ac():
void build_ac(){ queue q; q.push(0); int i,p,cur,son; while(!q.empty()){ p=q.front(); q.pop(); for(int i=0;i<4;i++){ if(tree[p].next[i]){ son=tree[p].next[i]; if(p==0)tree[son].fail=p; else { cur=tree[p].fail; while(cur&&tree[cur].next[i]==0)cur=tree[cur].fail; tree[son].fail=tree[cur].next[i]; } if(tree[cur]) tree[son].fail=0; q.push(son); } } }}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~