.题意是说给了N个带病毒的DNA串( DNA串只有AGCT几种单元组成)...再给一长串DNA..问这长串DNA最少改动几个(就是改..不是删除或者添加..)能保证没有包含病毒字串..输出这个最小改动的次数..若怎么修改都带病毒子串...输出-1...
思路:这是我第一道调bug调得快要发狂的代码,AC这道题花了我将近五个小时,md。其实思想很简单,就是将那些病毒DNA建成一棵AC自动机树,然后将输入了基因一个个代入AC树中。dp[i+1][son(j)]=min(dp[i+1][son(j)],dp[i][j]+(str[i]==char(son(j)))
稍微解释一下这个递推方程的意思,i表示输入第i个字符,j表示目前处在第j个结点,son(j)表示j结点的子节点。意思就是如果现在这个j的子节点字母和输入字母一样,那就代表没修改字母,就不用+1,不一样了,那说明要改,就+1.
然后讲一下我的错误:主要还是AC自动机血的不太熟练,而且以前练得模板也不太好。主要就是少了 if(temp&&temp->id) p->id=1;这么一句话,这样万一现在查询的字符串可能有病毒出现就会忽略掉了。例如
root
/ \
T A(flag=1)
/
A
/
G
那么输入TA的时候不是就把A是病毒这一点给忽略了么,所以要处理一下。以前模板用的是另外一种写法,我理解也不是很深刻.
我的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
char tp[30],str[2005];
int n,cnt;
struct node
{
int id,cnt;
node *next[4],*fail;
node()
{
for(int i=0;i<4;i++) next[i]=NULL;
id=0;
cnt=0;
fail=NULL;
}
}*s[2005],*root;
int dp[1005][2005];
int getval(char t)
{
switch(t)
{
case 'A': return 0;
case 'C': return 1;
case 'G': return 2;
case 'T': return 3;
}
}
char getc(int t)
{
switch(t)
{
case 0: return 'A';
case 1: return 'C';
case 2: return 'G';
case 3: return 'T';
}
}
void make_tree(int id)
{
int l=strlen(tp);
node *p=root;
for(int i=0;i<l;i++)
{
if(p->next[getval(tp[i])]==NULL)
{
p->next[getval(tp[i])]=new node();
p->next[getval(tp[i])]->cnt=cnt;
s[cnt++]=p->next[getval(tp[i])];
}
p=p->next[getval(tp[i])];
}
p->id=1;
}
void make_ac()
{
queue<node*> que;
node *p,*temp;
que.push(root);
while(!que.empty())
{
p=que.front();que.pop();
for(int i=0;i<4;i++)
{
if(p->next[i]!=NULL)
{
if(p==root) p->next[i]->fail=root;
else
{
temp=p->fail;
while(temp!=NULL)
{
if(temp->next[i]!=NULL)
{
p->next[i]->fail=temp->next[i];
break;
}
temp=temp->fail;
}
if(temp==NULL) p->next[i]->fail=root;
if(temp&&temp->id) p->id=1;
}
que.push(p->next[i]);
}else
{
if(p==root) p->next[i]=root;
else p->next[i]=p->fail->next[i];
}
}
}
}
int query(char *str,node *rt)
{
int i , j , k , l = strlen(str) , flag ;
memset(dp,inf,sizeof(dp)) ;
dp[0][0] = 0 ;
for(i = 0 ; i < l ; i++)
{
for(j = 0 ; j < cnt ; j++)
{
for(k = 0 ; k < 4 ; k++)
{
if( s[j]->next[k]->id ) continue ;
if( str[i] == getc(k) )
dp[i+1][ s[j]->next[k]->cnt ] = min( dp[i][j] , dp[i+1][ s[j]->next[k]->cnt ] );
else
dp[i+1][ s[j]->next[k]->cnt ] = min( dp[i][j]+1 , dp[i+1][ s[j]->next[k]->cnt ] );
}
}
}
int ans = inf ;
for(i = 0 ; i < cnt ; i++)
ans = min(ans,dp[l][i]) ;
if( ans == inf )
ans = -1 ;
return ans ;
}
int main()
{
int l,cas=0;
//freopen("t.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
cas++;
root=new node();
root->cnt=0;
s[0]=root;
cnt=1;
for(int i=1;i<=n;i++)
{
scanf("%s",tp);
//printf("%s\n",tp);
make_tree(i);
}
scanf("%s", str) ;
make_ac();
printf("Case %d: %d\n", cas , query(str,root) ) ;
}
return 0;
}
网上代码
.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std ;
#define INF 0x3f3f3f3f
struct node{
int flag , id ;
node *next[4] , *fail ;
} tree[2100] ;
queue <node *> que ;
int num , dp[1100][2100] ;
char str[2100] ;
char c[5] = "ACGT" ;
node *newnode()
{
node *p = &tree[num] ;
p->flag = 0 ;
p->id = num++ ;
p->fail = NULL ;
for(int i = 0 ; i < 4 ; i++)
p->next[i] = NULL ;
return p ;
}
void settree(char *s,node *rt)
{
int i , k , l = strlen(s) ;
node *p = rt ;
for(i = 0 ; i < l ; i++)
{
for(k = 0 ; k < 4 ; k++)
if( s[i] == c[k] )
break ;
if( p->next[k] == NULL )
p->next[k] = newnode() ;
p = p->next[k] ;
}
p->flag = 1 ;
return ;
}
void setfail(node *rt)
{
node *p = rt , *temp ;
p->fail = NULL;
while( !que.empty() ) que.pop() ;
que.push(p) ;
while( !que.empty() )
{
p = que.front() ;
que.pop() ;
for(int i = 0 ; i < 4 ; i++)
{
if( p->next[i] )
{
temp = p->fail ;
while( temp && !temp->next[i] )
temp = temp->fail ;
p->next[i]->fail = temp ? temp->next[i] : rt ;
que.push(p->next[i]) ;
if( temp && temp->flag )
p->flag = 1 ;
}
else
p->next[i] = p == rt ? rt : p->fail->next[i] ;
}
}
return ;
}
int query(char *s,node *rt)
{
int i , j , k , l = strlen(s) , flag ;
memset(dp,INF,sizeof(dp)) ;
dp[0][0] = 0 ;
for(i = 0 ; i < l ; i++)
{
for(j = 0 ; j < num ; j++)
{
for(k = 0 ; k < 4 ; k++)
{
if( tree[j].next[k]->flag ) continue ;
if( s[i] == c[k] )
dp[i+1][ tree[j].next[k]->id ] = min( dp[i][j] , dp[i+1][ tree[j].next[k]->id ] );
else
dp[i+1][ tree[j].next[k]->id ] = min( dp[i][j]+1 , dp[i+1][ tree[j].next[k]->id ] );
}
}
}
int ans = INF ;
for(i = 0 ; i < num ; i++)
ans = min(ans,dp[l][i]) ;
if( ans == INF )
ans = -1 ;
return ans ;
}
int main()
{
freopen("t.txt","r",stdin);
int i , n , temp = 1 ;
node *rt ;
while( scanf("%d", &n) && n )
{
num = 0 ;
rt = newnode() ;
for(i = 0 ; i < n ; i++)
{
scanf("%s", str) ;
settree(str,rt) ;
}
setfail(rt) ;
scanf("%s", str) ;
printf("Case %d: %d\n", temp++ , query(str,rt) ) ;
}
return 0 ;
}