题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2457
题目大意:给定n个危险DNA序列,再给一段长度长为L的DNA序列S,DNA序列S中可能包含危险DNA序列,可以改变S中的字符,改变一个算一次操作,问最少操作几次可使S不含危险DNA序列,如果怎么操作都会含有危险DNA序列输出-1。
解题思路:这题并不难,只是常规的ac自动机+DP,状态转移方程比较容易想,dp[i][j->next[k]] += min(dp[i-1][j] + (S[[i] == k))(dp[i][j]表示在我们构造解的过程中,长度为i且到j位置的最少操作数,不可达到值为inf)。为什么可以这样?S肯定也是由‘A','C','G','T'构造而成,那我们就模拟之,假设在长度为i的时候用A来构造,如果S[i]就是A,那就不需要操作,不相等则操作数加1.状态转移方程里面的k就是我们下一步要构造的字符的hash值,也对应着j的一个后继节点,并且这个后继节点是安全的。AC自动机里的节点记录了下一个可走的位置,顺着ac自动机里的next数组一步一步构造,上句话所说的模拟便成为可能,在构造解的过程一定不能经过危险节点(某个危险DNA序列的结束位置),这就保证了最后的解不包括危险DNA序列。
测试数据:
2
AAA
AAG
AAAG
A
TG
TGAATG
A
G
C
T
AGT
代码:
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define inf 2147482136
struct node {
int flag,in;
node *next[5],*fail;
}*q[500010],trie[1050];
char sub[100],str[1010];
int total,head,tail,dp[1010][1010];
inline int min(int a,int b) {
if (a < b) return a;
else return b;
}
inline int code (char c) {
switch(c)
{
case 'A': return 0;
case 'C': return 1;
case 'G': return 2;
case 'T': return 3;
}
return 0;
}
inline node *new_node() {
node *p = &trie[total];
p->in = total++;
p->flag = 0;
p->fail = NULL;
memset(p->next,0,sizeof(p->next));
return p;
}
void Insert(char *str,node *root) {
int i = 0,k;
node *p = root;
while (str[i]) {
k = code(str[i++]);
if (p->next[k] == NULL) p->next[k] = new_node();
p = p->next[k];
}
p->flag = 1;
}
void Auto_Ac(node *root) {
root->fail = NULL;
q[head++] = root;
while (head != tail) {
node *temp = q[tail++]; //获取头结点
for (int i = 0; i < 4; ++i) {
if (temp->next[i] != NULL) {
if (temp == root) temp->next[i]->fail = root;
else {
temp->next[i]->fail = temp->fail->next[i];
if (temp->next[i]->fail->flag == 1)
temp->next[i]->flag = 1;
}
q[head++] = temp->next[i];
}
else {
if (temp == root) temp->next[i] = root;
else temp->next[i] = temp->fail->next[i];
}
}//for i
}//while !=
}
int Query(char *str,node *root) {
int i,j,k,len = strlen(str);
node *p;
for (i = 0; i <= len; ++i)
for (j = 0; j < total; ++j)
dp[i][j] = inf;
dp[0][0] = 0;
for (i = 1; i <= len; ++i)
for (j = 0; j < total; ++j)
if (dp[i-1][j] < inf) {
//可达
for (k = 0; k < 4; ++k)
if (trie[j].next[k]->flag == 0) {
p = trie[j].next[k];
dp[i][p->in] = min(dp[i][p->in],dp[i-1][j] + (code(str[i-1]) != k));
}
}
int ans = inf;
for (i = 0; i < total; ++i)
if (trie[i].flag == 0 && dp[len][i] < ans)
ans = dp[len][i];
if (ans == inf) return -1;
else return ans;
}
int main()
{
int i,j,k,n,cas = 0;
while (scanf("%d",&n) ,n) {
total = head = tail = 0;
node *root = new_node();
for (i = 1; i <= n; ++i)
scanf("%s",sub),Insert(sub,root);
Auto_Ac(root);
scanf("%s",str);
printf("Case %d: %d\n",++cas,Query(str,root));
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。