POJ 3691 AcWing 1053 修复DNA

题解:

这题的做法为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 
#include 
#include 
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;
} 

你可能感兴趣的:(动态规划,AC自动机)