• HDU2457 DNA repair —— AC自动机 + DP


    题目链接:https://vjudge.net/problem/HDU-2457

    DNA repair

    Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 3126    Accepted Submission(s): 1661


    Problem Description
    Biologists finally invent techniques of repairing DNA that contains segments causing kinds of inherited diseases. For the sake of simplicity, a DNA is represented as a string containing characters 'A', 'G' , 'C' and 'T'. The repairing techniques are simply to change some characters to eliminate all segments causing diseases. For example, we can repair a DNA "AAGCAG" to "AGGCAC" to eliminate the initial causing disease segments "AAG", "AGC" and "CAG" by changing two characters. Note that the repaired DNA can still contain only characters 'A', 'G', 'C' and 'T'.

    You are to help the biologists to repair a DNA by changing least number of characters.
     
    Input
    The input consists of multiple test cases. Each test case starts with a line containing one integers N (1 ≤ N ≤ 50), which is the number of DNA segments causing inherited diseases.
    The following N lines gives N non-empty strings of length not greater than 20 containing only characters in "AGCT", which are the DNA segments causing inherited disease.
    The last line of the test case is a non-empty string of length not greater than 1000 containing only characters in "AGCT", which is the DNA to be repaired.

    The last test case is followed by a line containing one zeros.
     
    Output
    For each test case, print a line containing the test case number( beginning with 1) followed by the
    number of characters which need to be changed. If it's impossible to repair the given DNA, print -1.
     
    Sample Input
    2 AAA AAG AAAG 2 A TG TGAATG 4 A G C T AGT 0
     
    Sample Output
    Case 1: 1 Case 2: 4 Case 3: -1
     
    Source

    题意:

    给出n个遗传病DNA序列,以及一个人体DNA序列,问至少修改多少个脱氧核苷酸,使得人体DNA序列不含遗传病?

    题解:

    1.将n个序列插入AC自动机。

    2.设dp[i][j]为:处理到第i个字符,且当前状态为j(自动机上的状态)的最少修改数。

    3.AC自动机实际上是一张有向图,如果要求字符串不含有自动机里面的病毒,那么字符串只能沿着自动机上的边走,当然需要去除病毒结点。所以状态转移:当字符串中的第i个字符与状态j的字符相同,那么dp[i+1][newj] = dp[i][j],否则dp[i+1][newj] = dp[i][j] + 1,dp[i+1][newj]取最小值即可。

    代码如下:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <vector>
      6 #include <cmath>
      7 #include <queue>
      8 #include <stack>
      9 #include <map>
     10 #include <string>
     11 #include <set>
     12 using namespace std;
     13 typedef long long LL;
     14 const double EPS = 1e-6;
     15 const int INF = 2e9;
     16 const LL LNF = 9e18;
     17 const int MOD = 1e5;
     18 const int MAXN = 1e3+10;
     19 
     20 int Map[128];
     21 char M[4];
     22 int dp[MAXN][MAXN];
     23 struct Trie
     24 {
     25     int sz, base;
     26     int next[MAXN][4], fail[MAXN], end[MAXN];
     27     int root, L;
     28     int newnode()
     29     {
     30         for(int i = 0; i<sz; i++)
     31             next[L][i] = -1;
     32         end[L++] = false;
     33         return L-1;
     34     }
     35 
     36     void init(int _sz, int _base)
     37     {
     38         sz = _sz;
     39         base = _base;
     40         L = 0;
     41         root = newnode();
     42     }
     43     void insert(char buf[])
     44     {
     45         int len = strlen(buf);
     46         int now = root;
     47         for(int i = 0; i<len; i++)
     48         {
     49             if(next[now][Map[buf[i]]] == -1) next[now][Map[buf[i]]] = newnode();
     50             now = next[now][Map[buf[i]]];
     51         }
     52         end[now] |= true;
     53     }
     54     void build()
     55     {
     56         queue<int>Q;
     57         fail[root] = root;
     58         for(int i = 0; i<sz; i++)
     59         {
     60             if(next[root][i] == -1) next[root][i] = root;
     61             else fail[next[root][i]] = root, Q.push(next[root][i]);
     62         }
     63         while(!Q.empty())
     64         {
     65             int now = Q.front();
     66             Q.pop();
     67             end[now] |= end[fail[now]]; //当前串的后缀是否也包含单词
     68             for(int i = 0; i<sz; i++)
     69             {
     70                 if(next[now][i] == -1) next[now][i] = next[fail[now]][i];
     71                 else fail[next[now][i]] = next[fail[now]][i], Q.push(next[now][i]);
     72             }
     73         }
     74     }
     75 
     76     int query(char s[])
     77     {
     78         int len = strlen(s);
     79         for(int i = 0; i<=len; i++)
     80         for(int j = 0; j<L; j++)
     81             dp[i][j] = INF;
     82 
     83         dp[0][0] = 0;
     84         for(int i = 0; i<len; i++)
     85         for(int j = 0; j<L; j++)
     86         {
     87             if(end[j] || dp[i][j]==INF) continue;
     88             for(int k = 0; k<sz; k++)
     89             {
     90                 int newi = i+1;
     91                 int newj = next[j][k];
     92                 if(end[newj]) continue;
     93                 dp[newi][newj] = min(dp[newi][newj], dp[i][j]+(s[i]!=M[k]));
     94             }
     95         }
     96 
     97         int ret = INF;
     98         for(int i = 0; i<L; i++)
     99             ret = min(ret, dp[len][i]);
    100         return ret==INF?-1:ret;
    101     }
    102 };
    103 
    104 Trie ac;
    105 char buf[MAXN];
    106 int main()
    107 {
    108     Map['A'] = 0; Map['C'] = 1; Map['G'] = 2; Map['T'] = 3; //离散化
    109     M[0] = 'A'; M[1] = 'C'; M[2] = 'G'; M[3] = 'T';
    110     int n, kase = 0;
    111     while(scanf("%d", &n) && n)
    112     {
    113         ac.init(4,'A');
    114         for(int i = 1; i<=n; i++)
    115         {
    116             scanf("%s", buf);
    117             ac.insert(buf);
    118         }
    119         ac.build();
    120         scanf("%s", buf);
    121         int ans = ac.query(buf);
    122         printf("Case %d: %d
    ", ++kase, ans);
    123     }
    124     return 0;
    125 }
    View Code
  • 相关阅读:
    关于oracle当中数据类型转换的问题
    CASE WHEN的两种格式
    C#设置默认打印机
    运用Merge Into实现增加或更新数据
    增加或修改的存储过程
    深拷贝与浅拷贝
    sql server两种分页方法
    获取sql执行时间
    inserted触发器,一张表插入数据时,同时向另外一张表插入数据
    List<string[]> 如何去重
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8456548.html
Copyright © 2020-2023  润新知