现在的位置: 首页 > 综合 > 正文

POJ3691 第一道AC自动机+DP 指针版+总结

2013年11月04日 ⁄ 综合 ⁄ 共 3792字 ⁄ 字号 评论关闭

学了编译原理的自动机后理解起来容易多了,对于一个字符串我只关心最后几个字符组成的前缀是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;
}*/

 

 

 

抱歉!评论已关闭.