题目链接: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;
}