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;
}
原文地址:https://www.cnblogs.com/swm8023/p/2626809.html