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;
}
posted @ 2022-03-13 18:38  zyy2001  阅读(162)  评论(0编辑  收藏  举报