poj 3691(AC自動機,新模板)
阿新 • • 發佈:2018-12-12
.題意是說給了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 ; }