题解:
这题的做法为AC自动机和动态规划。
具体来说,我们先将不允许出现的短串建AC自动机,在每个串末尾打一个标记(注意:在当
n
x
t
[
p
]
nxt[p]
nxt[p],被打标记后,p上也要打标记,因为从根到nxt[p]所组成的字符串是从根到p所组成的字符串的后缀)
我们设置
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示处理(修改或不修改)长串的前i个字符,匹配到AC自动机中的j号节点的方案中修改字符数量的最小值为多少(也就是从根到j所组成的字符串为长串前i个字符所组成的字符串的后缀)。(不能匹配到打过标记的点)
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1005;
const int INF = (1 << 30);
int tr[N][4], q[N], idx;
int nxt[N], tag[N], dp[N][N];
char str[25];
char s[N];
int get(char c){
if(c == 'A') return 0;
if(c == 'G') return 1;
if(c == 'C') return 2;
if(c == 'T') return 3;
}
void insert(){
int p = 0, len = strlen(str), i, t;
for(i = 0; i < len; i++){
t = get(str[i]);
if(tr[p][t] == 0)
tr[p][t] = ++idx;
p = tr[p][t];
}
tag[p] = 1;
}
void build(){
int head = 1, tail = 1, i, t;
for(i = 0; i < 4; i++)
if(tr[0][i])
q[tail++] = tr[0][i];
while(head < tail){
t = q[head];
head++;
for(i = 0; i < 4; i++){
if(tr[t][i] == 0) tr[t][i] = tr[nxt[t]][i];
else {
nxt[tr[t][i]] = tr[nxt[t]][i];
q[tail++] = tr[t][i];
tag[tr[t][i]] |= tag[nxt[tr[t][i]]];
}
}
}
}
int main(){
int T = 0, n, m;
int i, j, k, p, ans;
while(1){
T++;
idx = 0;
memset(tr, 0, sizeof(tr));
memset(nxt, 0, sizeof(nxt));
memset(tag, 0, sizeof(tag));
scanf("%d", &n);
if(n == 0) break;
for(i = 1; i <= n; i++){
scanf("%s", str);
insert();
}
build();
scanf("%s", s + 1);
m = strlen(s + 1);
memset(dp, 120, sizeof(dp));
dp[0][0] = 0;
for(i = 0; i < m; i++){
for(j = 0; j < idx; j++){
for(k = 0; k < 4; k++){
p = tr[j][k];
if(tag[p] == 0) dp[i + 1][p] = min(dp[i + 1][p], dp[i][j] + (k != get(s[i + 1])));
}
}
}
ans = INF;
for(j = 0; j < idx; j++) ans = min(ans, dp[m][j]);
if(ans == INF) ans = -1;
printf("Case %d: %d\n", T, ans);
}
return 0;
}