您的位置:首页 > 其它

AC自动机与dp(poj3691)

2017-04-18 13:33 211 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: