1. 程式人生 > >poj 3691(AC自動機,新模板)

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 ;
}