You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
4.1 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1010; // 致病DNA片段的数量上限50每个DNA片段的长度不超过20所以Trie树的结点个数最多不超过50*20=1000个节点
int n, m;
int tr[N][4], cnt[N], idx; // Trie树每个节点最多四条出边ATGC映射就0123,cnt表示每个节点为结尾的结束标识idx配合创建Trie树的导航员
int q[N], ne[N]; // AC自动机需要的队列数组失配指针数组,ne[u]描述在u结点失配时找到root~u的最长后缀相同的最长前缀root~v
char str[N]; // 第一轮当作模板串的录入存储器完成构建Trie树后第二轮当做文本串的存储器
int f[N][N]; // f[i][j]:DNA扫描到第i个字母且当前走到了trie里面的第j个节点 的 所有操作方案中,最少的修改数量
int get(char c) {
if (c == 'A') return 0;
if (c == 'T') return 1;
if (c == 'G') return 2;
return 3;
}
void insert() { // 利用模板串构建Trie树
int p = 0;
for (int i = 0; str[i]; i++) {
int t = get(str[i]);
if (tr[p][t] == 0) tr[p][t] = ++idx;
p = tr[p][t];
}
cnt[p] = 1; // 记录结束标识
}
// 构建AC自动机
void bfs() {
int hh = 0, tt = -1;
for (int i = 0; i < 4; i++) // 利用bfs进行构建把第一层的节点入队列
if (tr[0][i]) q[++tt] = tr[0][i];
while (hh <= tt) {
int p = q[hh++];
for (int i = 0; i < 4; i++) { // 尝试四个出边
int &c = tr[p][i];
if (!c)
c = tr[ne[p]][i];
else {
ne[c] = tr[ne[p]][i];
q[++tt] = c;
// c是不是终止节点有两个判断来源
// 1、自己本身就是模示串A的终点
// 2、它的失配指针指向的结点是模示串B的终点
cnt[c] |= cnt[ne[c]];
}
}
}
}
int main() {
int T = 1;
while (scanf("%d", &n), n) {
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
memset(ne, 0, sizeof ne);
idx = 0;
for (int i = 0; i < n; i++) {
scanf("%s", str);
insert();
}
// 构建AC自动机
bfs();
// str:长度不超过1000的非空字符串字符串中仅包含字符A, G , C , T用以表示待修复DNA片段
// Q:为什么不直接从索引0开始读入呢
// 答你可以尝试以索引0开始读入你会发现后面的状态转移方程就无法使用f[i-1][j]
// 因为数组下标越界了~哈哈,看来啊,都是先有状态转移方程,后有初始值,枚举序等东西
scanf("%s", str + 1);
m = strlen(str + 1); // 长串的长度
memset(f, 0x3f, sizeof f); // 初始化动态规划数组,预求最小,先设最大
f[0][0] = 0; // 匹配了前0个字符在Trie树中的节点号是0的情况下此时代价为0
// 开始DP填表一维是长串走到哪个字符二维是AC自动机中走到哪个节点
for (int i = 0; i < m; i++) // 枚举长串字符
for (int j = 0; j <= idx; j++) // 枚举节点
for (int k = 0; k < 4; k++) { // AGCT四种字符
int t = get(str[i + 1]) != k; // 下一个字符str[i+1],如果与目标字符一致代价为0否则代价为1
int p = tr[j][k]; // fail失配指针指向位置
// 如果p节点不是病毒DNA的终止节点那么它的最小代价可以由f[i][j]+t转移
if (!cnt[p]) f[i + 1][p] = min(f[i + 1][p], f[i][j] + t);
}
// 找出最小值
int res = INF;
for (int i = 0; i <= idx; i++) res = min(res, f[m][i]);
// 没有最小值,返回-1
if (res == INF) res = -1;
printf("Case %d: %d\n", T++, res);
}
return 0;
}