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

题目传送门: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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值