POJ 3691 DNA repair ( Trie图 && DP )
题意 : 给出 n 个病毒串,最后再给出一个主串,问你最少改变主串中的多少个单词才能使得主串中不包含任何一个病毒串
分析 : 做多了AC自动机的题,就会发现这些题有些都是很套路的题目。在构建 Trie 图的时候给病毒串末尾打上标记,最后定义DP[i][j] = 长度为 i 的串在 j 这个状态节点最少改变数使得其不包含病毒串,则状态转移方程
if(Trie[k]不是被标记的病毒节点) DP[i+1][j] = min( DP[i+1][j], DP[i][k] + (mp[S[i]] != k) )
k 为 j 节点的四个下一状态,转到"ATGC"其中一个,而mp[]作用是把"ATGC"转为0、1、2、3
DP的初始状态为 DP[0][0] = 0,DP[0~len][0~节点数] = INF
具体的看代码即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<queue> #include<stdio.h> #include<string.h> using namespace std; const int Max_Tot = 1111; const int Letter = 4; const int INF = 0x3f3f3f3f; int mp[128]; struct Aho{ struct StateTable{ int Next[Letter]; int fail, flag; }Node[Max_Tot]; int Size; queue<int> que; inline void init(){ while(!que.empty()) que.pop(); memset(Node[0].Next, 0, sizeof(Node[0].Next)); Node[0].fail = Node[0].flag = 0; Size = 1; } inline void insert(char *s){ int now = 0; for(int i=0; s[i]; i++){ int idx = mp[s[i]]; if(!Node[now].Next[idx]){ memset(Node[Size].Next, 0, sizeof(Node[Size].Next)); Node[Size].fail = Node[Size].flag = 0; Node[now].Next[idx] = Size++; } now = Node[now].Next[idx]; } Node[now].flag = 1; } inline void BuildFail(){ Node[0].fail = 0; for(int i=0; i<Letter; i++){ if(Node[0].Next[i]){ Node[Node[0].Next[i]].fail = 0; que.push(Node[0].Next[i]); }else Node[0].Next[i] = 0;///必定指向根节点 } while(!que.empty()){ int top = que.front(); que.pop(); if(Node[Node[top].fail].flag) Node[top].flag = 1; for(int i=0; i<Letter; i++){ int &v = Node[top].Next[i]; if(v){ que.push(v); Node[v].fail = Node[Node[top].fail].Next[i]; }else v = Node[Node[top].fail].Next[i]; } } } }ac; char S[1111]; int dp[1111][1111]; int main(void) { mp['A'] = 0, mp['T'] = 1, mp['G'] = 2, mp['C'] = 3; int n, Case = 1; while(~scanf("%d", &n) && n){ ac.init(); for(int i=0; i<n; i++){ scanf("%s", S); ac.insert(S); } ac.BuildFail(); scanf("%s", S); int len = strlen(S); for(int i=0; i<=len; i++) for(int j=0; j<=ac.Size; j++) dp[i][j] = 2333; dp[0][0] = 0; for(int i=0; i<len; i++){ for(int j=0; j<ac.Size; j++){ if(dp[i][j] != 2333){ for(int k=0; k<4; k++){ int newi = i+1; int newj = ac.Node[j].Next[k]; if(!ac.Node[newj].flag){ dp[newi][newj] = min(dp[newi][newj], dp[i][j] + (k != mp[S[i]]) ); } } } } } int ans = 2333; for(int i=0; i<ac.Size; i++) ans = min(ans, dp[len][i]); printf("Case %d: ", Case++); if(ans != 2333) printf("%d\n", ans); else puts("-1"); } return 0; }