POJ 3691 DNA repair (AC自动机+dp)

题目类型 AC自动机+dp

题目意思
给出最多 50 个最长 20 的不好的字符串 问给出一个最长 1000 的字符串至少要修改多少个字符才可以使这个字符串不包含不好的字符串
字符串只含有(A,G,C,T)这四种字符

解题方法
用输入的 不好的字符串 构造ac自动机 (结点数不会超过 1000) 然后进行普通的匹配过程, 简单地理解为在AC自动机上进行移动
需要注意的地方
1.不能移动到那些如果走到这里意味着已经和某不好的串匹配成功了的 状态结点
2.由于可以修改字符所以可以移动到原本不能移动到的 状态结点 但是 cost 要 +1
仔细想下可以发现 前 i 个字符的解可以由前 i-1 个字符得到的解推出来 即可用 dp 解
dp[i][j] 使前i个字符不包含不好的字符串且最终落在 状态结点 j 上的最少修改次数
dp[i+1][child[j]] = dp[i][j] + ( edge(j,child[j]) != str(i+1) ) (其中 edge(j,child[j])的意思是 j 结点到 child[j] 结点这条边代表的字符,如果这个字符与当前正在匹配的 str(i+1)字符不等的话显然就要把 str(i+1) 修改成该字符 str(i+1)指要修改到满足要求的那个字符串的第i+1个字符 相等的话直接转移即可)

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

#define B printf("BUG!!\n");
const int INF = 1<<29;

char str[60][30];
char s[1100];
int dp[1100][1100];
int f[1100];
int last[1100];
bool vis[1100];

struct Trie {
	int ch[1100][30];
	int val[1100];
	int sz;
	int n_kind;

	void init() {
		n_kind = 4;
		sz = 1;
		memset(ch[0], 0, sizeof(ch[0]));
		memset(val, 0, sizeof(val));
		memset(last, 0, sizeof(last));
	}

	int mp(char c) {
		if(c == 'A') return 0; if(c == 'G') return 1;
		if(c == 'C') return 2; return 3;
	}

	void insert(char * s) {
		int len = strlen(s);
		int u = 0;
		for( int i=0; i<len; i++ ) {
			int id = mp(s[i]);
			if(ch[u][id] == 0) {
				memset(ch[sz], 0, sizeof(ch[sz]));
				ch[u][id] = sz++;
			}
			u = ch[u][id];
		}
		val[u] = 1;
	}

	void build_ac() {
		queue<int>q;
		f[0] = 0;
		for( int i=0; i<n_kind; i++ ) {
			if(ch[0][i]) {
				f[ch[0][i]] = 0;
				q.push(ch[0][i]);
			}
		}
		while(!q.empty()) {
			int u = q.front(); q.pop();
			for( int i=0; i<n_kind; i++ ) {
				if(ch[u][i] == 0) {
					ch[u][i] = ch[f[u]][i];
					continue;
				}
				int tu = u;
				while(tu && ch[f[tu]][i] == 0) tu = f[tu];
				f[ch[u][i]] = ch[f[tu]][i];
				last[ch[u][i]] = val[ch[f[tu]][i]] ? ch[f[tu]][i] : last[ch[f[tu]][i]];
				q.push(ch[u][i]);
			}
		}
	}

	int solve() {
		int len = strlen(s);
		int ans = INF;
		for( int i=0; i<=len; i++ ) for( int j=0; j<sz; j++ ) dp[i][j] = INF;
		dp[0][0] = 0;
		for( int i=1; i<=len; i++ ) {
			int id = mp(s[i-1]);
			for( int j=0; j<sz; j++ ) {
				for( int k=0; k<n_kind; k++ ) {
					int next = ch[j][k];
					if(last[next] || val[next]) continue;
					dp[i][next] = min(dp[i][next], dp[i-1][j] + (id != k));
					if(i == len) ans = min(ans, dp[i][next]);
				}
			}
		}
		return ans;
	}

	/*void count() {
		int len = strlen(s), u = 0;
		int cnt = 0;
		for( int i=0; i<len; i++ ) {
			int id = s[i] - 'A';
			u = ch[u][id];
			if(val[u]) {
				printf("i = %d\n", i+1);
				cnt++;
				int tu = u;
				while(last[tu]) { cnt++; tu = last[tu]; }
			}
		}
		printf("%d\n", cnt);
	}*/
}trie;

int main() {
	freopen("in2", "r", stdin);
	int n, cnt = 1;
	while(scanf("%d", &n), n) {
		trie.init();
		for( int i=0; i<n; i++ ) { scanf("%s", str[i]); trie.insert(str[i]); }
		trie.build_ac();
		scanf("%s", s);
		int ans = trie.solve();
		printf("Case %d: ", cnt++);
		if(ans == INF) printf("-1\n");
		else printf("%d\n", ans);
	}
	return 0;
}


你可能感兴趣的:(dp,AC自动机)