1053. 修复DNA
题目链接
1053. 修复DNA
生物学家终于发明了修复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≤N≤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
解题思路
ac自动机+dp
-
状态表示:\(f[i][j]\) 表示串的第 \(i\) 位字母对应ac自动机上的状态 \(j\) 时的不含模式串的最少修复数量
-
状态计算:\(f[i+1][p]=min(f[i+1][p],f[i][j])\),其中 \(i+1\) 上的状态 \(p\) 由 \(i\) 上的状态 \(j\) 转移过来,为满足要求,要求状态节点 \(j\) 和 \(p\) 不能为模式串对应的节点
-
时间复杂度:\(O(20nm)\)
代码
// Problem: 修复DNA
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/1055/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1005;
int trie[N][4],q[N],ne[N],n,idx,f[N][N];
bool dan[N];
char s[N];
int get(char c)
{
if(c=='A')return 0;
if(c=='G')return 1;
if(c=='C')return 2;
return 3;
}
void insert()
{
int p=0;
for(int i=0;s[i];i++)
{
int t=get(s[i]);
if(!trie[p][t])trie[p][t]=++idx;
p=trie[p][t];
}
dan[p]=true;
}
void build()
{
int hh=0,tt=-1;
for(int i=0;i<4;i++)
if(trie[0][i])q[++tt]=trie[0][i];
while(hh<=tt)
{
int t=q[hh++];
for(int i=0;i<4;i++)
{
int p=trie[t][i];
if(!p)trie[t][i]=trie[ne[t]][i];
else
{
ne[p]=trie[ne[t]][i];
dan[p]|=dan[ne[p]];
q[++tt]=p;
}
}
}
}
int main()
{
int T=0;
while(cin>>n,n)
{
memset(dan,0,sizeof dan);
idx=0;
memset(trie,0,sizeof trie);
memset(ne,0,sizeof ne);
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++)
{
cin>>s;
insert();
}
build();
cin>>(s+1);
int m=strlen(s+1);
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 cost=get(s[i+1])!=k;
int p=trie[j][k];
if(!dan[p]&&!dan[j])f[i+1][p]=min(f[i+1][p],f[i][j]+cost);
}
int res=0x3f3f3f3f;
for(int i=0;i<idx;i++)res=min(res,f[m][i]);
printf("Case %d: %d\n",++T,res==0x3f3f3f3f?-1:res);
}
return 0;
}