Hdu 2457 DNA repair (字符串_AC自动机(DP))

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457


题目大意:给定n个危险DNA序列,再给一段长度长为L的DNA序列S,DNA序列S中可能包含危险DNA序列,可以改变S中的字符,改变一个算一次操作,问最少操作几次可使S不含危险DNA序列,如果怎么操作都会含有危险DNA序列输出-1。


解题思路:这题并不难,只是常规的ac自动机+DP,状态转移方程比较容易想,dp[i][j->next[k]] += min(dp[i-1][j] + (S[[i] == k))(dp[i][j]表示在我们构造解的过程中,长度为i且到j位置的最少操作数,不可达到值为inf)。为什么可以这样?S肯定也是由‘A','C','G','T'构造而成,那我们就模拟之,假设在长度为i的时候用A来构造,如果S[i]就是A,那就不需要操作,不相等则操作数加1.状态转移方程里面的k就是我们下一步要构造的字符的hash值,也对应着j的一个后继节点,并且这个后继节点是安全的。AC自动机里的节点记录了下一个可走的位置,顺着ac自动机里的next数组一步一步构造,上句话所说的模拟便成为可能,在构造解的过程一定不能经过危险节点(某个危险DNA序列的结束位置),这就保证了最后的解不包括危险DNA序列。


测试数据:

2
AAA
AAG

AAAG    


2
A
TG

TGAATG


4
A
G
C
T
AGT


代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define inf 2147482136


struct node {

    int flag,in;
    node *next[5],*fail;
}*q[500010],trie[1050];
char sub[100],str[1010];
int  total,head,tail,dp[1010][1010];


inline int min(int a,int b) {

    if (a < b) return a;
    else return b;
}

inline int code (char c) {

    switch(c)
    {
    case 'A': return 0;
    case 'C': return 1;
    case 'G': return 2;
    case 'T': return 3;
    }
    return 0;
}

inline node *new_node() {
    
    node *p = &trie[total];
    p->in = total++;
    p->flag = 0;
    p->fail = NULL;
    memset(p->next,0,sizeof(p->next));

    return p;
}

void Insert(char *str,node *root) {

    int i = 0,k;
    node *p = root;


    while (str[i]) {

        k = code(str[i++]);
        if (p->next[k] == NULL) p->next[k] = new_node();

        p = p->next[k];
    }

    p->flag = 1;
}

void Auto_Ac(node *root) {

    root->fail = NULL;
    q[head++] = root;


    while (head != tail) {
        

        node *temp = q[tail++];        //获取头结点
        for (int i = 0; i < 4; ++i) {

            if (temp->next[i] != NULL) {
                if (temp == root) temp->next[i]->fail = root;
                else {

                    temp->next[i]->fail = temp->fail->next[i];
                    if (temp->next[i]->fail->flag == 1)
                        temp->next[i]->flag = 1;
                }
                q[head++] = temp->next[i];
            }
            else {

                if (temp == root) temp->next[i] = root;
                else temp->next[i] = temp->fail->next[i];
            }
        }//for i
    }//while  !=
}

int Query(char *str,node *root) {

    int i,j,k,len = strlen(str);
    node *p;


    for (i = 0; i <= len; ++i)
        for (j = 0; j < total; ++j)
            dp[i][j] = inf;


    dp[0][0] = 0;
    for (i = 1; i <= len; ++i)
        for (j = 0; j < total; ++j)
            if (dp[i-1][j] < inf) {
            //可达
                for (k = 0; k < 4; ++k)
                    if (trie[j].next[k]->flag == 0) {

                        p = trie[j].next[k];
                        dp[i][p->in] = min(dp[i][p->in],dp[i-1][j] + (code(str[i-1]) != k)); 
                    }
            }

    int ans = inf;
    for (i = 0; i < total; ++i)
        if (trie[i].flag == 0 && dp[len][i] < ans)
            ans = dp[len][i];

    if (ans == inf) return -1;
    else return ans;
}


int main()
{
    int i,j,k,n,cas = 0;


    while (scanf("%d",&n) ,n) {

        total = head = tail = 0;
        node *root = new_node();
        for (i = 1; i <= n; ++i)
            scanf("%s",sub),Insert(sub,root);

        Auto_Ac(root);
        scanf("%s",str);
        printf("Case %d: %d\n",++cas,Query(str,root));
    }
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值