[POJ3691]DNA repair AC自动机+dp

题目

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

题意:给出n个带病毒的DNA与一个将要修改的DNA,要求修改后不能与任何一个带病毒的DNA相匹配,问最少修改次数。修改一次即为改动一个字符(可以为A,G,C,T)。

思路:考虑dp,用dp[i][j]表示当前已经判断到了待修改DNA的第i位,且在AC自动机上已经成功地匹配到了第j号点。

我们枚举将要把待修改DNA的第i为修改成的字符k,显然,如果k与原来的第i+1位相同,则不用修改。

这道题的关键是如何在修改DNA后,在AC自动机上找到下一次匹配的位置,只有找到这个位置,我们才能成功地进行状态转移。

用一个数组nextnext[j][k]表示当前在AC自动机上的j号点,将下一位修改成k后,下一次在AC自动机上匹配的点的编号。我们先暂时不讨论转移后是否是带病毒节点,那么:

1.如果当前节点有字符为k的后继节点,直接将next[j][k]赋值成它即可。
2.否则,就到当前节点的fail节点去找
3.如果当前节点的fail节点是一个“危险节点”,则将当前节点也标记为危险节点(它能匹配上病毒串)

for (int i=0;i<4;i++) {
    if (trie[now][i]) {
        int f=fail[now];
        while (f&&!trie[f][i]) f=fail[f];
        int son=trie[now][i];
        fail[son]=trie[f][i],q.push(son);
        next[now][i]=trie[now][i];
        val[son]|=val[fail[son]];
    }
    else next[now][i]=next[fail[now]][i];
}

dp转移方程

int Dp(char s[],int n) {
    memset(dp,1,sizeof(dp));
    dp[0][0]=0;
    for (int i=0;i

完整代码如下

#include 
#include 
#include 
#include 
using namespace std;
int next[1010][4],trie[1010][4],val[1010],ncnt;
int idx(char c) {
    if (c=='A') return 0;
    if (c=='G') return 1;
    if (c=='C') return 2;
    return 3;
}
void init() {memset(trie[0],0,sizeof(trie[0])),memset(next[0],0,sizeof(next[0])),memset(val,0,sizeof(val)),ncnt=0;}
void insert(char s[],int len) {
    int now=0;
    for (int i=1;i<=len;i++) {
        int c=idx(s[i]);
        if (!trie[now][c]) {
            now=trie[now][c]=++ncnt;
            memset(trie[now],0,sizeof(trie[now]));
        }
        else now=trie[now][c];
    }
    val[now]=1;
}
queue q;
int fail[1010];
void bfs() {
    while (!q.empty()) q.pop();
    for (int i=0;i<4;i++) 
        if (trie[0][i]) q.push(trie[0][i]),fail[trie[0][i]]=0,next[0][i]=trie[0][i];
        else next[0][i]=0;
    while (!q.empty()) {
        int now=q.front();q.pop();
        for (int i=0;i<4;i++) {
            if (trie[now][i]) {
                int f=fail[now];
                while (f&&!trie[f][i]) f=fail[f];
                int son=trie[now][i];
                fail[son]=trie[f][i],q.push(son);
                next[now][i]=trie[now][i];
                val[son]|=val[fail[son]];
            }
            else next[now][i]=next[fail[now]][i];
        }
    }
}
int dp[1010][1010],n;
int Dp(char s[],int n) {
    memset(dp,1,sizeof(dp));
    dp[0][0]=0;
    for (int i=0;i

你可能感兴趣的:([POJ3691]DNA repair AC自动机+dp)