給定 N (≤ 50) 個致病 DNA 片段(模式串,長度 ≤ 20,字符集 {A,G,C,T})和一條待修復 DNA 主串(長度 ≤ 1000)。允許修改主串中的字符,求最少修改次數使得修復後的主串不包含任何模式串作為子串。無解輸出 -1。
struct ACAutomaton {
static const int MAXN = 1010, CH = 4;
int ch[MAXN][CH], fail[MAXN], danger[MAXN], sz;
int idx(char c) { return c=='A'?0:c=='G'?1:c=='C'?2:3; }
void init() { sz = 1; memset(ch[0], 0, sizeof ch[0]); danger[0]=0; }
void insert(const char *s) {
int u = 0;
for (; *s; s++) {
int c = idx(*s);
if (!ch[u][c]) {
ch[u][c] = sz;
memset(ch[sz], 0, sizeof ch[sz]);
danger[sz] = 0;
sz++;
}
u = ch[u][c];
}
danger[u] = 1; // 模式串結尾
}
void build() {
queue q;
for (int c = 0; c < CH; c++)
if (ch[0][c]) q.push(ch[0][c]);
else ch[0][c] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
danger[u] |= danger[fail[u]]; // 傳播危險標記
for (int c = 0; c < CH; c++) {
if (ch[u][c]) {
fail[ch[u][c]] = ch[fail[u]][c];
q.push(ch[u][c]);
} else {
ch[u][c] = ch[fail[u]][c];
}
}
}
}
} ac;
int dp[2][1010];
void solve() {
int L = strlen(str+1);
memset(dp, 0x3f, sizeof dp);
dp[0][0] = 0;
for (int i = 0; i < L; i++) {
int cur = i&1, nxt = (i+1)&1;
memset(dp[nxt], 0x3f, sizeof dp[nxt]);
for (int u = 0; u < ac.sz; u++) if (dp[cur][u] < INF) {
for (int c = 0; c < 4; c++) {
int v = ac.ch[u][c];
if (ac.danger[v]) continue;
int cost = dp[cur][u] + (c != ac.idx(str[i+1]));
dp[nxt][v] = min(dp[nxt][v], cost);
}
}
}
int ans = INF;
for (int u = 0; u < ac.sz; u++) ans = min(ans, dp[L&1][u]);
if (ans == INF) ans = -1;
printf("Case %d: %d\n", ++caseNum, ans);
}
本題是「多模式串匹配 + 序列編輯最小代價」的經典組合。AC 自動機將多模式匹配轉化為確定性有限自動機(DFA),DP 在 DFA 上尋找避開終止狀態的最短路徑。掌握「Trie + Fail + DP」三步曲,可解決大類相似問題(如 HDU 2222、POJ 2778 等)。