学了编译原理的自动机后理解起来容易多了,对于一个字符串我只关心最后几个字符组成的前缀是AC自动机中的哪个状态节点,然后在AC自动机上进行转移。
我们可以先构造一个AC自动机,然后把各个节点之间的合法联系做成矩阵,这样就方便后面查询了。
需要注意的地方:
1、判断一个节点是否合法,除了看自身外还要看其父节点和其fail指向的第一个节点。(考虑到子串包含的原因)
2、打合法关系表时,打出合法关系要保证出发点和到达点都是合法的。
3、针对这题我的代码来说,DP过程要注意最后i==len,也就是处理DP[S.length()][j]时,要特殊判断一下,因为已经不能继续更新了,目测用优先队列写就不会出现这问题,因为第一次访问到DP[S.lenth()][j]时就可以直接返回答案了。
PS:算上调试代码,266行,敲的时候感觉有点儿畏首畏尾,生怕哪出点儿问题,若是加上比赛时压力,以我目前的水平,我觉得比赛时我是敲不出来的...
代码如下:
#include<iostream> #include<string> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; #define maxc 4 #define INF 0x7fffffff int ACCnt; int Trans[2005][5];//记录AC自动机上的状态转移 //bool IsBad[2005]; struct Node { char ch; int id; bool is; //记录当前节点是否非法,由父节点以及各个fail节点决定 Node *child[maxc]; Node *fail; Node () { ch=' '; id=ACCnt++; is=false; for(int i=0;i<maxc;++i) { child[i]=NULL; } fail=NULL; } Node (char c) { ch=c; id=ACCnt++; is=false; for(int i=0;i<maxc;++i) { child[i]=NULL; } fail=NULL; } }; Node *Root; void Init() { ACCnt=0; memset(Trans,-1,sizeof(Trans)); // memset(IsBad,false,sizeof(IsBad)); Root=new Node(); } int ID(char c) { if(c=='A') return 0; if(c=='T') return 1; if(c=='C') return 2; if(c=='G') return 3; } void Insert(char chs[]) { // cout<<"entry"<<endl; int i,id,len=strlen(chs); char c; Node *t=Root; for(i=0;i<len;++i) { c=chs[i]; id=ID(c); // cout<<i<<" "<<c<<" "<<id<<endl; if(t->child[id]==NULL) t->child[id]=new Node(c); t=t->child[id]; } t->is=true; } void DeleteTrie(Node *t) { int i; for(i=0;i<maxc;++i) { if(t->child[i]!=NULL) DeleteTrie(t->child[i]); } delete t; } void PreAC() { // cout<<"AC"<<endl; queue<Node*> q; q.push(Root); int i; Node *p,*t; while(!q.empty()) { p=q.front(); q.pop(); // if(p->is) // IsBad[p->id]=true; // cout<<p->id<<" "<<p->is<<endl; for(i=0;i<maxc;++i) { if(p->child[i]!=NULL) { // cout<<p->child[i]->id<<endl; t=p->fail; while(t!=NULL&&t->child[i]==NULL) t=t->fail; if(t==NULL) p->child[i]->fail=Root; else p->child[i]->fail=t->child[i]; p->child[i]->is|=((p->is)|(p->child[i]->fail->is)); q.push(p->child[i]); } } } } void PreTrans() { queue<Node*> q; q.push(Root); int i; Node *p,*t; while(!q.empty()) { p=q.front(); q.pop(); if(p->is) continue; for(i=0;i<maxc;++i) { if(p->child[i]!=NULL) q.push(p->child[i]); t=p; while(t!=NULL&&t->child[i]==NULL) t=t->fail; if(t==NULL) t=Root; else t=t->child[i]; if(!(t->is)) Trans[p->id][i]=t->id; } } } void Show() { int i,j; for(i=0;i<ACCnt;++i) { cout<<i<<":"; for(j=0;j<maxc;++j) cout<<" "<<Trans[i][j]; cout<<endl; } } string S; int DP[1005][2005]; bool Inq[1005][2005]; int Work() { memset(DP,-1,sizeof(DP)); memset(Inq,false,sizeof(Inq)); DP[0][0]=0; queue<int> q1,q2; q1.push(0),q2.push(0); Inq[0][0]=true; int i,len,state,state2,val; // cout<<"Work init complete"<<endl; while(!q1.empty()) { len=q1.front(),state=q2.front(); q1.pop(),q2.pop(); // cout<<"len: "<<len<<" "<<"state: "<<state<<endl; Inq[len][state]=false; if(len==S.length()) continue; for(i=0;i<maxc;++i) { if(Trans[state][i]==-1) continue; state2=Trans[state][i]; // cout<<"new state: "<<state2<<endl; if(DP[len+1][state2]==-1) DP[len+1][state2]=DP[len][state]+(ID(S[len])!=i); else DP[len+1][state2]=min(DP[len+1][state2],DP[len][state]+(ID(S[len])!=i)); if(!Inq[len+1][state2]) { Inq[len+1][state2]=true; q1.push(len+1),q2.push(state2); } } } int ans=INF; len=S.length(); for(i=0;i<ACCnt;++i) if(DP[len][i]!=-1) ans=min(ans,DP[len][i]); return ans; } int main() { int n,i,ans,cas=0; char chs[25]; while(scanf("%d",&n)!=EOF) { if(n==0) break; cas++; Init(); // cout<<"Init complete"<<endl; while(n--) { scanf("%s",chs); Insert(chs); // cout<<"Insert complete"<<endl; } PreAC(); // cout<<"PreAC complete"<<endl; PreTrans(); // cout<<"PreTrans complete"<<endl; // cout<<"State number: "<<ACCnt<<endl; // Show(); cin>>S; ans=Work(); if(ans==INF) printf("Case %d: -1\n",cas); else printf("Case %d: %d\n",cas,ans); DeleteTrie(Root); } return 0; } /*int Find(char T[]) { int i,id,ans=0,len=strlen(T); Node *p,*t; p=Root; char c; for(i=0;i<len;++i) { c=T[i]; id=c-'a'; while(p!=NULL&&p->child[id]==NULL) p=p->fail; if(p==NULL) p=Root; else { p=p->child[id]; t=p; while(t!=NULL) { if(t->is) ans++; t=t->fail; } } } return ans; }*/