AC自动机与dp(poj3691)

1.题目大意

给你一段DNA,求最少修改多少基因可以让DNA不含带病基因(基因只能是G,T,C,A)

2.做法简述

AC自动机+dp,用f[i][j]表示在i节点处匹配DNA中j处基因的最少修改次数,那么方程是:

f[son[i]][j]=min(f[son[i]][j],f[i][j-1] or f[i][j-1]+1);

如果son[i]处基因和DNAj处相同则不要加1,否则要加1

3.代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,sz,ans,inf=199999999;
char s[22],ss[1005];
int a[10005][5],ne[10005],f[10005][1005],que[10005];
bool dan[10005];//注意初始化
int chan(char x){//转换字符
	if(x=='A')return 1;
	else if(x=='G')return 2;
	else if(x=='C')return 3;
	else if(x=='T')return 4;
}
void tri(){//构建trie树
	int i,j,l=strlen(s),from=1,sum;
	for(i=0;i<l;i++){
		sum=chan(s[i]);
		if(a[from][sum])from=a[from][sum];
		else {
			sz++;a[from][sum]=sz;from=sz;
			for(j=1;j<=4;j++)a[sz][j]=0;
			ne[sz]=dan[sz]=0;
		}
	}
	dan[from]=1;
}
void ac(){//构建ac自动机
	int i,j,head=1,tail=1,from;
	que[1]=1;
	while(head<=tail){
		from=que[head];
		for(i=1;i<=4;i++){
			if(!a[from][i])continue;
			j=ne[from];
			while(!a[j][i])j=ne[j];
			ne[a[from][i]]=a[j][i];
			if(dan[a[j][i]])dan[a[from][i]]=1;
			tail++;que[tail]=a[from][i];
		}
		head++;
	}
}
void solve(){//进行dp
	int i,j,k,from,sum,l=strlen(ss);
	for(i=0;i<l;i++){//i+1
		sum=chan(ss[i]);
		for(j=1;j<=sz;j++){
			if(dan[j]||f[j][i]>inf)continue;
			for(k=1;k<=4;k++){
				from=j;
				while(!a[from][k])from=ne[from];
				if(k==sum)f[a[from][k]][i+1]=min(f[j][i],f[a[from][k]][i+1]);
				else f[a[from][k]][i+1]=min(f[j][i]+1,f[a[from][k]][i+1]);
			}
		}
	}
}
int main()
{
   	int i,j,cnt=0,l;
   	while(1){
   		scanf("%d",&n);sz=1;ans=inf;
   		if(n==0)break;
   		for(i=1;i<=4;i++){a[0][i]=1;a[1][i]=0;}
   		ne[1]=dan[1]=0;
   		for(i=1;i<=n;i++){scanf("%s",s);tri();}ac();
   		memset(f,127/3,sizeof(f));
   		f[1][0]=0;scanf("%s",ss);
   		solve();l=strlen(ss);
   		for(i=1;i<=sz;i++)if(!dan[i])ans=min(ans,f[i][l]);
   		cnt++;
   		if(ans<inf)printf("Case %d: %d\n",cnt,ans);
   		else printf("Case %d: -1\n",cnt);
   	}
   	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值