HDU3341 Lost's revenge【AC自动机 + DP(状态编号)】

题目描述:

N(<=50)个有效基因串,每个长度<=10,你有的基因串M,长度<=40,重排M,使得其包含的有效基因串数最多(可重叠),输出最多个数。
基因只有四个字母AGCT。

题目分析:

有效基因串建立AC自动机。
f [ a ] [ b ] [ c ] [ d ] [ i ] f[a][b][c][d][i] f[a][b][c][d][i]表示分别用了 a , b , c , d a,b,c,d a,b,c,d个字母A,G,C,T,当前在自动机的 i i i号点得到的最多有效基因数,枚举添加字符转移,最坏复杂度 O ( 1 1 4 ∗ 500 ∗ 4 ) O(11^4*500*4) O(1145004)

f f f显然不能直接开五维数组,可以枚举状态进行编号,直接dfs也是比较方便的,预处理出每个状态转移到的下一个状态的编号,五维就可以缩为 f [ 1 1 4 ] [ i ] f[11^4][i] f[114][i]

编号也可以通过变进制,记A G C T的总数分别为 s 1 , s 2 , s 3 , s 4 s_1,s_2,s_3,s_4 s1,s2,s3,s4,那么 ( a , b , c , d ) (a,b,c,d) (a,b,c,d)就可以表示为 a + b ∗ ( s 1 + 1 ) + c ∗ ( s 1 + 1 ) ( s 2 + 1 ) + d ∗ ( s 1 + 1 ) ( s 2 + 1 ) ( s 3 + 1 ) a+b*(s_1+1)+c*(s_1+1)(s_2+1)+d*(s_1+1)(s_2+1)(s_3+1) a+b(s1+1)+c(s1+1)(s2+1)+d(s1+1)(s2+1)(s3+1)。最好在DP前把当前状态的下一个状态的编号存在数组里,避免在DP里面再去求下一个状态,减小常数。

Code:

#include<bits/stdc++.h>
#define maxn 505
#define maxc 4
using namespace std;
int T,n,t[4],ch[maxn][maxc],fail[maxn],cnt[maxn],tot,f[15005][maxn],nxt[15005][maxc],sum;
char s[45];
inline int Num(char c){return c=='A'?0:c=='G'?1:c=='C'?2:3;}
void Clear(){
	memset(ch,0,(tot+1)*maxc*4),memset(fail,0,(tot+1)*4);
	memset(cnt,0,(tot+1)*4),memset(t,0,sizeof t),tot=0;
}
void insert(char *s){
	int r=0,v;
	for(int i=0;s[i];i++,r=ch[r][v]) if(!ch[r][v=Num(s[i])]) ch[r][v]=++tot;
	cnt[r]++;
}
int q[maxn],L,R;
void build(){
	q[L=R=0]=0;
	while(L<=R){
		int r=q[L++],c; cnt[r]+=cnt[fail[r]];
		for(int i=0;i<maxc;i++)
			if(c=ch[r][i]) fail[c]=r?ch[fail[r]][i]:0,q[++R]=c;
			else ch[r][i]=ch[fail[r]][i];
	}
}
void solve(){
	int a[4],b[4]={1,t[0]+1,(t[0]+1)*(t[1]+1),(t[0]+1)*(t[1]+1)*(t[2]+1)};
	for(a[0]=0;a[0]<=t[0];a[0]++) for(a[1]=0;a[1]<=t[1];a[1]++) for(a[2]=0;a[2]<=t[2];a[2]++) for(a[3]=0;a[3]<=t[3];a[3]++){
		int now=a[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];
		for(int i=0;i<4;i++) nxt[now][i]=a[i]<t[i]?now+b[i]:-1;
	}
	sum=b[3]*(t[3]+1)-1,memset(f,-1,(sum+1)*maxn*4),f[0][0]=0;
	for(int s=0;s<sum;s++) for(int i=0;i<=tot;i++) if(~f[s][i])
		for(int j=0,x,c;j<4;j++) if(~(x=nxt[s][j]))
			c=ch[i][j],f[x][c]=max(f[x][c],f[s][i]+cnt[c]);
	printf("Case %d: %d\n",++T,*max_element(f[sum],f[sum]+tot+1));
}
int main()
{
	while(scanf("%d",&n),n){
		for(int i=1;i<=n;i++) scanf("%s",s),insert(s);
		build();
		scanf("%s",s);
		for(int i=0;s[i];i++) t[Num(s[i])]++;
		solve(),Clear();;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值