HDU 2457 DNA repair (AC自动机 + DP)

31 篇文章 0 订阅
26 篇文章 0 订阅

题目链接:DNA repair


解析:给出n个致病DNA序列,给一段DNA片段,问最少修改多少个碱基才能修复这段DNA序列中的所有致病序列。

AC自动机 + DP。

将n个致病DNA序列构成一个自动机。

令DP[i][j]表示长度为i走到节点j是所需改变的最少个数。

状态转移时,枚举下一步所有可能的碱基,然后判断该碱基是否达到匹配状态,若能,则安全转移,继续枚举下一个碱基;否则在不匹配的前提下,看该碱基加入之后是否跟上一状态相同,若不同,则需修复,即计数加一。若相同,直接转移即可。然后选择其中最小的修复个数即可。



AC代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

const int INF = 0x3f3f3f3f;

struct Trie{
    int next[1010][4], fail[1010];
    bool end[1010];
    int root, L;

    int newnode(){
        for(int i = 0; i < 4; i++) next[L][i] = -1;
        end[L++] = false;
        return L-1;
    }

    void init(){
        L = 0;
        root = newnode();
    }

    int getch(char ch){
        if(ch == 'A') return 0;
        if(ch == 'C') return 1;
        if(ch == 'G') return 2;
        else return 3;
    }

    void insert(char s[]){
        int len = strlen(s);
        int now = root;
        for(int i = 0; i < len; i++){
            if(next[now][getch(s[i])] == -1)
                next[now][getch(s[i])] = newnode();
            now = next[now][getch(s[i])];
        }
        end[now] = true;
    }

    void build(){
        queue<int> Q;
        for(int i = 0; i < 4; i ++){
            if(next[root][i] == -1) next[root][i] = root;
            else{
                fail[ next[root][i] ] = root;
                Q.push(next[root][i]);
            }
        }
        while(!Q.empty()){
            int now = Q.front();
            Q.pop();
            if(end[ fail[now] ] == true) end[now] = true;    //注意
            for(int i = 0; i < 4; i ++){
                if(next[now][i] == -1)
                    next[now][i] = next[ fail[now] ][i];
                else{
                    fail[ next[now][i] ] = next[ fail[now] ][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }

    int dp[1010][1010];
    int solve(char buf[]){
        int len = strlen(buf);
        for(int i=0; i<=len; i++)
            for(int j=0; j<L; j++)
                dp[i][j] = INF;
        dp[0][root] = 0;
        for(int i=0; i<len; i++)
            for(int j=0; j<L; j++)
                if(dp[i][j] < INF){
                    for(int k=0; k<4; k++){
                        int newj = next[j][k];
                        if(end[newj]) continue;       //可以安全转移
                        int tmp;
                        if(k == getch(buf[i])) tmp = dp[i][j];    //相同,无需修复
                        else tmp = dp[i][j] + 1;    //不同,修复次数加一
                        dp[i+1][newj] = min(dp[i+1][newj], tmp);
                    }
                }
        int ans = INF;
        for(int i=0; i<L; i++)
            ans = min(ans, dp[len][i]);    //选择最小的修复次数
        if(ans == INF) ans = -1;
        return ans;
    }
};

Trie ac;
char buf[1010];

int main(){
    #ifdef sxk
        freopen("in.txt", "r", stdin);
    #endif //sxk

    int n;
    int kase = 0;
    while(scanf("%d", &n) == 1 && n){
        ac.init();
        for(int i = 0; i < n; i ++){
            scanf("%s", buf);
            ac.insert(buf);
        }
        ac.build();
        scanf("%s", buf);
        printf("Case %d: %d\n", ++kase, ac.solve(buf));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值