HDU2457 DNA repair [AC自动机+DP]
给出一个DNA串以及若干个带有疾病的串,问至少要改变DNA串中的几个字符,才能让它不携带病毒串。其实还是不包含若干子串的问题,可以转化为走一条不包含病毒串的路径,这条路径组成的串和给出DNA串最少相差几个字符,这样就很容易想到DP方程
d[i][j]一开始初始化为无穷大,x是j的父亲节点,也就是说存在next[x][p]=j,一般都是从前向后刷表的。flag表示这一位和DNA串中的对应字符是否相等,相等时为0,不相等时为1。
注意在走的过程中不要走到危险节点上去。
#include <string.h> #include <stdio.h> #define MAXN 1001 #define INF 0x3fffffff char s[MAXN]; int n; int next[MAXN][4],fail[MAXN],flag[MAXN],pos; int calk(char c){ if(c=='A')return 0; if(c=='G')return 1; if(c=='T')return 2; return 3; } int newnode(){ for(int i=0;i<4;i++)next[pos][i]=0; fail[pos]=flag[pos]=0; return pos++; } void insert(char *s){ int p=0; for(int i=0;s[i];i++){ int k=calk(s[i]),&x=next[p][k]; p=x?x:x=newnode(); } flag[p]=1; } int q[MAXN],front,rear; void makenext(){ q[front=rear=0]=0,rear++; while(front<rear){ int u=q[front++]; for(int i=0;i<4;i++){ int v=next[u][i]; if(v==0)next[u][i]=next[fail[u]][i]; else q[rear++]=v; if(u&&v){ fail[v]=next[fail[u]][i]; if(flag[fail[v]])flag[v]=1; } } } } int d[2][MAXN],cal[MAXN]; inline int min(int x,int y){return x<y?x:y;} int dp(char *s){ int len=strlen(s),cur=0; for(int i=0;i<len;i++)d[0][i]=0; for(int i=0;i<pos;i++)cal[i]=0; //cal表示这个点是否计算过,没有计算过的点不进行遍历孩子操作 d[0][0]=0,cal[0]=1; for(int i=0;i<len;i++){ cur^=1; for(int u=0;u<pos;u++)d[cur][u]=INF; for(int u=0;u<pos;u++){ if(!cal[u]||flag[u])continue; for(int k=0;k<4;k++){ int v=next[u][k]; if(flag[v])continue; cal[v]=1; d[cur][v]=min(d[cur][v],d[cur^1][u]+((calk(s[i])==k)?0:1)); } } } int ans=INF; for(int u=0;u<pos;u++)ans=min(ans,d[cur][u]); return ans!=INF?ans:-1; } int main(){ //freopen("test.in","r",stdin); int cas=1; while(scanf("%d",&n),n){ pos=0;newnode(); for(int i=0;i<n;i++){ scanf("%s",s); insert(s); } makenext(); scanf("%s",s); int ans=dp(s); printf("Case %d: %d\n",cas++,ans); } return 0; }