题目链接:点击打开链接
题目大意:给出n个带有遗传病的DNA序列和目标DNA序列,可以修改目标序列上若干字符使得目标串上不含有带有遗传病的DNA序列,修改尽可能少的字符串。
分析:我们以这n个遗传病序列建一颗tire树,把树上的每个节点看做是一种状态,定义dp(i,j)为前i个字符串在j状态下为目标串(不含遗传病序列)时需修改的最少次数,这样在建完树后遍历一遍树上的节点,维护一下dp数组就可以了。
实现代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 0x7fffffff
#define maxn 1010
struct node
{
int next[maxn][4];
int fail[maxn];
bool terminal[maxn];
int root,size;
int newnode()
{
for(int i=0;i<4;i++) next[size][i]=-1;
terminal[size++]=false;
return size-1;
}
void init()
{
size=0;
root=newnode();
}
int change(char c)
{
if(c=='A') return 0;
if(c=='C') return 1;
if(c=='G') return 2;
return 3;
}
void insert(char *str)
{
int p=root;
int i=0;
while(str[i])
{
int index=change(str[i]);
if(next[p][index]==-1)
next[p][index]=newnode();
p=next[p][index];
i++;
}
terminal[p]=true;
}
void build_fail()
{
queue <int> que;
fail[root]=root;
for(int i=0;i<4;i++)
{
if(next[root][i]==-1)
next[root][i]=root;
else
{
fail[ next[root][i] ]=root;
que.push(next[root][i]);
}
}
while(!que.empty())
{
int p=que.front();
que.pop();
if(terminal[ fail[p] ]) terminal[p]=true;
for(int i=0;i<4;i++)
{
if(next[p][i]==-1)
next[p][i]=next[ fail[p] ][i];
else
{
fail[ next[p][i] ]=next[ fail[p] ][i];
que.push(next[p][i]);
}
}
}
}
int dp[maxn][maxn];
int solve(char *str)
{
int len=strlen(str);
for(int i=0;i<=len;i++)
for(int j=0;j<size;j++)
dp[i][j]=INF;
dp[0][root]=0;
for(int i=0;i<len;i++)
for(int j=0;j<size;j++)
if(dp[i][j]<INF)
for(int k=0;k<4;k++)
{
int cnt=next[j][k];
if(terminal[cnt]) continue;
int temp;
if(k==change(str[i])) temp=dp[i][j];
else temp=dp[i][j]+1;
dp[i+1][cnt]=min(dp[i+1][cnt],temp);
}
int ans=INF;
for(int i=0;i<size;i++)
if(ans>dp[len][i]) ans=dp[len][i];
if(ans==INF) return -1;
return ans;
}
}AC;
int main()
{
char str[maxn];
int n,T=1;
while(scanf("%d",&n)&&n)
{
AC.init();
while(n--)
{
scanf("%s",str);
AC.insert(str);
}
AC.build_fail();
scanf("%s",str);
printf("Case %d: %d\n",T++,AC.solve(str));
}
return 0;
}