<—点个赞吧QwQ
宣传一下算法提高课整理
生物学家终于发明了修复DNA的技术,能够将包含各种遗传疾病的DNA片段进行修复。
为了简单起见,DNA看作是一个由’A’, ‘G’ , ‘C’ , ‘T’构成的字符串。
修复技术就是通过改变字符串中的一些字符,从而消除字符串中包含的致病片段。
例如,我们可以通过改变两个字符,将DNA片段”AAGCAG”变为”AGGCAC”,从而使得DNA片段中不再包含致病片段”AAG”,”AGC”,”CAG”,以达到修复该DNA片段的目的。
需注意,被修复的DNA片段中,仍然只能包含字符’A’, ‘G’ , ‘C’ , ‘T’。
请你帮助生物学家修复给定的DNA片段,并且修复过程中改变的字符数量要尽可能的少。
输入格式
输入包含多组测试数据。
每组数据第一行包含整数N,表示致病DNA片段的数量。
接下来N行,每行包含一个长度不超过20的非空字符串,字符串中仅包含字符’A’, ‘G’ , ‘C’ , ‘T’,用以表示致病DNA片段。
再一行,包含一个长度不超过1000的非空字符串,字符串中仅包含字符’A’, ‘G’ , ‘C’ , ‘T’,用以表示待修复DNA片段。
最后一组测试数据后面跟一行,包含一个0,表示输入结束。
输出格式
每组数据输出一个结果,每个结果占一行。
输入形如”Case x: y”,其中x为测试数据编号(从1开始),y为修复过程中所需改变的字符数量的最小值,如果无法修复给定DNA片段,则y为”-1”。
数据范围
$1 \le N \le 50$
输入样例:
2
AAA
AAG
AAAG
2
A
TG
TGAATG
4
A
G
C
T
AGT
0
输出样例:
Case 1: 1
Case 2: 4
Case 3: -1
思路
看到题目要保证字符串不存在任一 $n$ 个字符串,也就是多模式串匹配要失败,不难想到 AC 自动机。
考虑前面一题的状态,不难设计出状态 $f_{i,j}$ 表示前 $i$ 个字母,当前匹配到 AC 自动机中第 $j$ 个字母的最小修改次数。
考虑枚举下一个要填的字母,设填完字母后匹配的位置为 $p$,那么显然 $f_{i,j}$ 是 $f_{i+1,p}$ 的决策。
答案:$\displaystyle\min_{i=0}^{idx}f_{m,i}$,其中 $idx$ 是 AC 自动机的点数。
其实和上面一题基本没有区别。
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010;
int test_case = 1;
int n,m;
char str[N];
int q[N];
int tr[N][4],fail[N],flag[N],idx;
int f[N][N];
int get (char x) {
if (x == 'A') return 0;
if (x == 'G') return 1;
if (x == 'C') return 2;
return 3;
}
void insert () {
int u = 0,len = strlen (str + 1);
for (int i = 1;i <= len;i++) {
int k = get (str[i]);
if (!tr[u][k]) tr[u][k] = ++idx;
u = tr[u][k];
}
flag[u] = true;
}
void build_ACAM () {
int hh = 0,tt = -1;
for (int i = 0;i < 4;i++) {
if (tr[0][i]) q[++tt] = tr[0][i];
}
while (hh <= tt) {
int u = q[hh++];
for (int i = 0;i < 4;i++) {
int p = tr[u][i];
if (!p) tr[u][i] = tr[fail[u]][i];
else {
fail[p] = tr[fail[u]][i];
q[++tt] = p;
flag[p] |= flag[fail[p]];
}
}
}
}
int main () {
while (cin >> n,n) {
memset (flag,false,sizeof (flag));
memset (tr,0,sizeof (tr));
memset (fail,0,sizeof (fail));
idx = 0;
for (int i = 1;i <= n;i++) {
cin >> str + 1;
insert ();
}
cin >> str + 1;
m = strlen (str + 1);
build_ACAM ();
memset (f,0x3f,sizeof (f));
f[0][0] = 0;
for (int i = 0;i < m;i++) {
for (int j = 0;j <= idx;j++) {
for (int k = 0;k < 4;k++) {
int t = get (str[i + 1]) != k,p = tr[j][k];
if (!flag[p]) f[i + 1][p] = min (f[i + 1][p],f[i][j] + t);
}
}
}
int ans = 0x3f3f3f3f;
for (int i = 0;i <= idx;i++) ans = min (ans,f[m][i]);
if (ans == 0x3f3f3f3f) ans = -1;
cout << "Case " << test_case++ << ": " << ans << endl;
}
return 0;
}
太强了/bx/bx/bx/bx
有进阶的总结吗