poj 3691(AC自动机,新模板)

.题意是说给了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 ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值