hdu3341:Lost's revenge (AC自动机+DP)

xiaoxiao2021-02-28  96

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3341

题目大意:有不超过50个模式串,每个模式串长度不超过10。现在给出一个长度不超过40的串,要求将其重组(即可以打乱字符的顺序),最大化其得分。一个串的得分为:第一个模式串在该串中出现的次数+第二个模式串在该串中出现的次数……,其中每一个模式串可以重叠,如AAA中AA出现了两次。所有串中只有AGCT四种字符,不超过30组数据。

题目分析:我们可以发现:能获得的最大得分只跟匹配串中的AGCT的个数有关。我们对所有模式串建一个trie图,每一个模式串的结尾val++,并且每一个节点P获得它所有fail节点的val,表示如果走到节点P,则已经自动匹配完以它fail结尾的模式串。然后记P->f[x][y][z][w]表示已经用去x个A,y个G,z个C,w个T,转移到它的儿子。

然而我们机智的发现,如果有500个节点,这样开f数组,空间肯定爆炸。考虑到本题中是AGCT的个数之和<=40,而不是每一个都是40,有用的状态顶多11^4个。我们可以用hash的方法将四元组(x,y,z,w)映射到一个一位数组上,这样空间降到500*11^4。

后来我又参考了一下网上大神的题解,发现了一种更为简洁的方法,四元组(x,y,z,w)->x*(ng+1)*(nc+1)*(nt+1)+y*(nc+1)*(nt+1)+z*(nt+1)+w,其中ng,nc,nt分别表示G,C,T的个数。感觉有点像进制或乘法原理,第一位用nt+1进制,第二位用nc+1进制……总之好写了很多。

CODE:

#include<iostream> #include<string> #include<cstring> #include<cmath> #include<cstdio> #include<cstdlib> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=500; const int maxs=11*11*11*11+11; struct Tnode { int f[maxs]; int val; Tnode *son[4],*fsto; } tree[maxn]; Tnode *Root; int cur; Tnode *que[maxn]; int head,tail; int na,ng,nc,nt,ms; char s[maxn][maxn]; int n,cas=0; Tnode *New_node() { cur++; for (int i=0; i<ms; i++) tree[cur].f[i]=-1; tree[cur].val=0; for (int i=0; i<4; i++) tree[cur].son[i]=NULL; tree[cur].fsto=NULL; return tree+cur; } void Insert(char *t) { Tnode *P=Root; int len=strlen(t); for (int i=0; i<len; i++) { int to=0; if (t[i]=='G') to=1; if (t[i]=='C') to=2; if (t[i]=='T') to=3; if (!P->son[to]) P->son[to]=New_node(); P=P->son[to]; } P->val++; } void Init() { for (int i=1; i<=n; i++) scanf("%s",&s[i]); scanf("%s",&s[n+1]); int len=strlen(s[n+1]); na=ng=nc=nt=0; for (int i=0; i<len; i++) { if (s[n+1][i]=='A') na++; if (s[n+1][i]=='G') ng++; if (s[n+1][i]=='C') nc++; if (s[n+1][i]=='T') nt++; } ms=(na+1)*(ng+1)*(nc+1)*(nt+1); cur=-1; Root=New_node(); for (int i=1; i<=n; i++) Insert(s[i]); } void Bfs() { head=0,tail=1; que[tail]=Root; while (head<tail) { Tnode *F=que[++head]; for (int i=0; i<4; i++) if (F->son[i]) { Tnode *P=F->son[i],*Node=F->fsto; while ( Node && !Node->son[i] ) Node=Node->fsto; if (Node) P->fsto=Node->son[i]; else P->fsto=Root; P->val+=P->fsto->val; que[++tail]=P; } } for (int i=0; i<4; i++) if (!Root->son[i]) Root->son[i]=Root; for (int i=2; i<=tail; i++) for (int j=0; j<4; j++) if (!que[i]->son[j]) que[i]->son[j]=que[i]->fsto->son[j]; } void Work(int x,int y,int id) { for (int i=1; i<=tail; i++) { Tnode *P=que[i]; if (P->f[x]!=-1) { int &z=P->son[id]->f[y]; z=max(z,P->f[x]+P->son[id]->val); } } } void DP() { Root->f[0]=0; for (int s=0; s<ms-1; s++) { int t=s; int a=t/((ng+1)*(nc+1)*(nt+1)); t-=(a*(ng+1)*(nc+1)*(nt+1)); int g=t/((nc+1)*(nt+1)); t-=(g*(nc+1)*(nt+1)); int c=t/(nt+1); t-=(c*(nt+1)); if (a<na) Work(s,s+(ng+1)*(nc+1)*(nt+1),0); if (g<ng) Work(s,s+(nc+1)*(nt+1),1); if (c<nc) Work(s,s+(nt+1),2); if (t<nt) Work(s,s+1,3); } int ans=0; for (int i=1; i<=tail; i++) ans=max(ans,que[i]->f[ms-1]); printf("Case %d: %d\n",cas,ans); } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); while ( scanf("%d",&n),n ) { cas++; Init(); Bfs(); DP(); n=0; } return 0; }

转载请注明原文地址: https://www.6miu.com/read-66211.html

最新回复(0)