AC自动机与dp(poj3691)

来源:互联网 发布:淘宝网电脑网页版登陆 编辑:程序博客网 时间:2024/06/11 23:36

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(){//进行dpint i,j,k,from,sum,l=strlen(ss);for(i=0;i<l;i++){//i+1sum=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;} 


1 0