题目: http://acm.hdu.edu.cn/showproblem.php?pid=2457
题意:
给n个患病DNA串;
给1个DNA串,问最少修改几个(只能用字符AGCT)能使得所有患病DNA串未出现过。
分析:
AC自动机上做动态规划。
就像在Trie上走,去构造一个串;
设f[i][j]表示长度为i,Trie树上节点为j时最少替换的字符数;
则答案为min(f[len][j]), j遍历Trie树上所有节点。
对于每一个状态f[i][j],枚举下一位放置的字符k;
令u=ch[j][k];
若s[i+1]==k,则表明无需替换字符,f[i+1][u]=min(f[i+1][u], f[i][j]);
若s[i+1]!=k,则表明需要替换此位字符,f[i+1][u]=min(f[i+1][u], f[i][j]+1);
一开始对f数组置为无穷大inf;
另外,若一个节点为单词结尾,则其f一定为inf;
若一个节点的fail串上有单词结尾节点,则其f也为inf。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long llong;
const int tmax=1005;
const int inf=0x3f3f3f3f;
int n;
queue<int> Q;
int index(char x)
{
if(x=='A') return 0;
if(x=='G') return 1;
if(x=='C') return 2;
return 3;
}
struct AC{
int ch[tmax][4],f[tmax],cnt,last[tmax];
bool val[tmax];
void init()
{
cnt=0;
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
memset(f,0,sizeof(f));
memset(last,-1,sizeof(last));
return;
}
void insert(char *s)
{
int len=strlen(s),u=0,id;
for(int i=0;i<len;i++)
{
id=index(s[i]);
if(ch[u][id]==0)
ch[u][id]=++cnt;
u=ch[u][id];
}
val[u]=true;
return;
}
void getfail()
{
int i,x,u,v;
Q.push(0);
while(!Q.empty())
{
x=Q.front();Q.pop();
for(int i=0;i<4;i++)
{
u=ch[x][i];
if(!u)
{
ch[x][i]=ch[f[x]][i];
continue;
}
Q.push(u);
if(x==0) continue;
v=f[x];
while(v&&!ch[v][i]) v=f[v];
f[u]=ch[v][i];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
return;
}
}ac;
int f[tmax][tmax];
void work(char *s)
{
memset(f,0x3f,sizeof(f));
f[0][0]=0;
int len=strlen(s),u,tmp;
for(int i=0;i<len;i++)
{
for(int j=0;j<=ac.cnt;j++)
{
if(f[i][j]==inf) continue;
for(int k=0;k<4;k++)
{
u=ac.ch[j][k];
if(ac.val[u]||ac.last[u]!=-1) continue;
if(index(s[i])==k) tmp=f[i][j];
else tmp=f[i][j]+1;
f[i+1][u]=min(f[i+1][u],tmp);
}
}
}
int ans=inf;
for(int i=0;i<=ac.cnt;i++)
ans=min(ans,f[len][i]);
printf("%d\n",ans==inf?-1:ans);
return;
}
int main()
{
char s[tmax];
for(int kase=1;;kase++)
{
scanf("%d",&n);
if(n==0) break;
ac.init();
for(int i=1;i<=n;i++)
{
scanf("%s",s);
ac.insert(s);
}
ac.getfail();
scanf("%s",s);
printf("Case %d: ",kase);
work(s);
}
return 0;
}