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
原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8456548.html