NOI2011 阿狸的打字机题解

此题很明显的AC自动机。。。

先介绍一个神奇的性质:如果我们将AC自动机的Fail指针全部反向,将得到一棵由Fail指针组成的Fail Tree。对于模式串i来说,它的最后一个字母在Fail Tree中的节点的后代的数量与该串在其它串中出现的次数相等。

然后我们得到一个离线算法:

将询问双关键字排序,在FailTree上做一次DFS,得到DFS序。za再用树状数组维护前缀和。

具体做法:扫描原串,遇到小写字母,将相应的DFS序的位置+1,遇到P询问,B将字母弹出。

由于这个题还有许多细节,详细请看代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int MAXSIZE=26;
const int MAXN=210000;
struct Node{
    int Flag;
    Node *Next[MAXSIZE],*Fail,*Fa;
    Node(){
        Flag=0;
        memset(Next,0,sizeof(Next));
        //Fail=0;
        Fa=0;
    }
}*Rt,*Path[MAXN],e[MAXN];
Node *C=&e[0];
Node *New_Node(){
    Node *p=++C;
    return p;
}
struct Ask{
    int u,v,Num,Ans;
    void Input(){
        scanf("%d%d",&v,&u);
    }
    void Init(int u_,int v_,int Num_){
        u=u_; v=v_; Num=Num_;
    }
}Data[MAXN];
bool cmp(const Ask &a,const Ask &b){
    return a.u<b.u||(a.u==b.u&&a.v<b.v);
}
bool cmp1(const Ask &a,const Ask &b){
    return a.Num<b.Num;
}
struct GNode{
    int v;
    GNode *Next;
};
struct Graph{
    GNode *adj[MAXN];
    GNode edge[MAXN];
    GNode *Cnt1;
    Graph(){
        memset(adj,0,sizeof(adj));
        memset(edge,0,sizeof(edge));
        Cnt1=&edge[0];
    }
    int Size[MAXN],Dfn[MAXN],Cnt;
    void addedge(int u,int v){
        GNode *p=++Cnt1;
        p->v=v;
        p->Next=adj[u];
        adj[u]=p;
    }
    void DFS(int u){
        Size[u]=1;
        Dfn[u]=++Cnt;
        for(GNode *p=adj[u];p;p=p->Next){
            int v=p->v;
            if(Dfn[v]) continue;
            DFS(v);
            Size[u]+=Size[v];
        }
    }
}FTree;
char Cmd[MAXN*10];
int n,Len;
void Build(){
    queue<Node*> q;
    //Rt=new Node;
    q.push(Rt);
    while(!q.empty()){
        Node *p=q.front();
        q.pop();
        for(int i=0;i<MAXSIZE;++i){
            if(p->Next[i]){
                Node *x=p->Fail;
                while(x){
                    if(x->Next[i]){
                        p->Next[i]->Fail=x->Next[i];
                        break;
                    }
                    x=x->Fail;
                }
                if(!x){
                    p->Next[i]->Fail=Rt;
                }
                x=p->Next[i]->Fail;
                FTree.addedge(p->Next[i]->Flag,x->Flag);
                FTree.addedge(x->Flag,p->Next[i]->Flag);
                q.push(p->Next[i]);
            }
        }
    }
    FTree.DFS(0);
}
char Que[MAXN];
int F,R,Cnt,Pos[MAXN],wCnt,Orz;
Node *Osu;
void Insert(int L,int R,int Num){
    Node *p=Osu;
    for(int i=L;i<=R;++i){
        int Hehe=Que[i]-'a';
        if(!p->Next[Hehe]){
            p->Next[Hehe]=New_Node();
            p->Next[Hehe]->Flag=++Cnt;
            p->Next[Hehe]->Fa=p;
            Path[Cnt]=p;
        }
        if(i==R){
            Pos[Num]=p->Next[Hehe]->Flag;
            Osu=p->Next[Hehe];
        }
        p=p->Next[Hehe];
    }
    Orz=Orz+(R-L+1);
    //Osu=p->Fa;
}
int Tree[MAXN];
void Add(int x,int Val){
    while(x<=FTree.Cnt){
        Tree[x]+=Val;
        x+=x&-x;
    }
}
int GetSum(int x){
    int Ret=0;
    while(x){
        Ret+=Tree[x];
        x-=x&-x;
    }
    return Ret;
}
int ACnt=1;
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("type1.in","r",stdin);
    Rt=New_Node();
    scanf("%s",&Cmd[1]);
    Len=strlen(Cmd+1);
    //printf("%d\n",Len);
    Osu=Rt;
    //Osu->Fa=Rt;
    int Pre=0;
    for(int i=1;i<=Len;++i){
        if(Cmd[i]>='a'&&Cmd[i]<='z'){
            Que[R++]=Cmd[i];
        }
        else{
            if(Cmd[i]=='P'){
                ++wCnt;//
                Insert(Pre,R-1,wCnt);
                //F=R=0;
                Pre=R;
            }
            else{
                R--;
                Pre--;
                Osu=Osu->Fa;
            }
        }
    }
    /*if(F<R){
        wCnt++;
        Insert(F,R-1,wCnt);
    }*/
    //printf("%d\n",Orz);
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        Data[i].Input();
        Data[i].Init(Data[i].u,Data[i].v,i);
        //Quer.addedge(Data[i].u,Data[i].v);
    }
    Build();
    sort(Data+1,Data+n+1,cmp);
    F=R=0;
    int Hehe=0;
    Pre=0;
    Osu=Rt;
    for(int i=1;i<=Len;++i){
        if(Cmd[i]>='a'&&Cmd[i]<='z'){
            Que[R++]=Cmd[i];
        }
        else{
            if(Cmd[i]=='P'){
                ++Hehe;
                Node *p=Osu;
                for(int j=Pre;j<R;++j){
                    int N=Que[j]-'a';
                    if(p->Next[N]){
                        Add(FTree.Dfn[p->Next[N]->Flag],1);
                    }
                    p=p->Next[N];
                }
                Osu=p;
                Pre=R;
                //for(int j=ACnt;j<=n;++j){
                while(ACnt<=n){
                    if(Data[ACnt].u>Hehe)
                        break;
                    else if(Data[ACnt].u==Hehe){
                        int L=FTree.Dfn[Pos[Data[ACnt].v]],R=L+FTree.Size[Pos[Data[ACnt].v]]-1;
                        //L++;
                        Data[ACnt].Ans=GetSum(R)-GetSum(L-1);
                    }
                    ACnt++;
                }
                /*p=Rt;
                for(int j=F;j<R;++j){
                    int N=Que[j]-'a';
                    if(p->Next[N])
                        Add(FTree.Dfn[p->Next[N]->Flag],-1);
                    p=p->Next[N];
                }*/
            }
            if(Cmd[i]=='B'){
                R--;
                Pre--;
                Add(FTree.Dfn[Osu->Flag],-1);
                Osu=Osu->Fa;
            }
        }
    }
    sort(Data+1,Data+n+1,cmp1);
    for(int i=1;i<=n;++i)
        printf("%d\n",Data[i].Ans);
return 0;
}

posted @ 2013-04-18 16:04  Return_0  阅读(273)  评论(0编辑  收藏  举报