HDU2457 (DNA repair)[AC自动机,动态规划]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457

题目大意:给定n个模板串,再给一个待匹配串,问至少修改待匹配串的几个字符能使得它不包含任何一个模板串

可以想到一个满足条件的串一定能在trie图上沿边一直走却遇不到任何一个单词节点(或者能通过后缀链接跳转到单词节点的点),而这个trie图事实上构成了一个状态转移图,我们把它应用到DP的状态转移上。
令dp[i][j]表示处理到第i个字符且停留在trie图上第j个状态时的最小修改次数,则状态转移方程为dp[i+1][ch[j][c]]=min(dp[i][ch[j][c]],dp[i][j]+(idx(s[i])!=c))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=1009;
const int inf=0x3f3f3f3f;

struct Trie {
int ch[maxn][4];
int f[maxn];
bool danger[maxn];
int dp[maxn][maxn];
int sz;
void init() {
sz=1;
memset(ch[0],0,sizeof(ch[0]));
memset(f,0,sizeof(f));
memset(danger,0,sizeof(danger));
}
inline int idx(char c) {
if(c=='A') return 0;
if(c=='G') return 1;
if(c=='C') return 2;
if(c=='T') return 3;
return -1;
}
void insert(char *s) {
int u=0,n=strlen(s);
for(int i=0;i<n;i++) {
int c=idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz],0,sizeof(ch[sz]));
danger[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
danger[u]=true;
}
void getFail() {
queue<int> q;
while(!q.empty()) q.pop();
f[0]=0;
for(int c=0;c<4;c++) {
int u=ch[0][c];
if(u) { f[u]=0;q.push(u); }
}
while(!q.empty()) {
int r=q.front(); q.pop();
for(int c=0;c<4;c++) {
int u=ch[r][c];
if(!u) { ch[r][c]=ch[f[r]][c];continue; }
q.push(u);
int v=f[r];
while(v && !ch[v][c]) v=f[v];
f[u]=ch[v][c];
danger[u]|=danger[ch[v][c]];
}
}
}
int getans(char *s) {
int n=strlen(s);
for(int i=0;i<=n;i++) {
for(int j=0;j<sz;j++) {
dp[i][j]=inf;
}
}
dp[0][0]=0;
for(int i=0;i<n;i++) {
for(int j=0;j<sz;j++) {
if(dp[i][j]>=inf) continue;
for(int k=0;k<4;k++) {
if(danger[ch[j][k]]) continue;
dp[i+1][ch[j][k]]=min(dp[i+1][ch[j][k]],dp[i][j]+(idx(s[i])!=k));
}
}
}
int ans=inf;
for(int i=0;i<sz;i++) {
ans=min(ans,dp[n][i]);
}
if(ans==inf) return -1;
return ans;
}
}tr;

char s[30];
char str[1009];
int main() {
int n;
int cas=0;
while(~scanf("%d",&n)) {
if(n==0) break;
tr.init();
for(int i=0;i<n;i++) {
scanf("%s",s);
tr.insert(s);
}
tr.getFail();
scanf("%s",str);
printf("Case %d: %d\n",++cas,tr.getans(str));
}
}