BZOJ3881: [Coci2015]Divljak

来源:互联网 发布:数据库设计实例 教材 编辑:程序博客网 时间:2024/04/24 23:40

BZOJ3881: [Coci2015]Divljak

AC自动机·Fail树

题解:

一开始想错了,还是对AC自动机的性质太不熟悉了。。。

以前没接触过Fail树,先来简单总结一下。
Fail树就是只保留AC自动机的fail指针并将其反向建出来的树。
对于Fail树上的节点,它代表的字符串是其子树中的节点代表的字符串的后缀。也就是下面的包含上面的。

考虑这样一个问题:假设给你若干个字符串,每次询问a在b中出现了多少次?可以把字符串建AC自动机,建Fail树,让b在AC自动机上走,走到的点+1,最后查询一下a在Fail树上的子树和即可。处理下一个询问之前别忘了把+1的点清零。

这道题的不同在于,询问是a在几个b中出现过,就是说a在一个b中出现很多次也只能贡献1。因此可以让b在AC自动机上走,走到的点+1,然后把走到的点按dfs序排序,相邻两个的lca处-1。查询a的子树和即是答案。

Code:

#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>#include <queue>#define D(x) cout<<#x<<" = "<<x<<"  "#define E cout<<endlusing namespace std;const int N = 2000005;int n,q; char str[N]; int tp[N];int ch[N][26],fail[N],ptr[N],sz=1;int pa[N][25],dep[N],pos[N],end[N],tim;int insert(char s[]){    int x=1;    for(int i=0;s[i]!='\0';i++){        if(!ch[x][s[i]-'a']){            ch[x][s[i]-'a']=++sz;        }        x=ch[x][s[i]-'a'];    }    return x;}void acBuild(){    queue<int> q; q.push(1); int f;    while(!q.empty()){        int u=q.front(); q.pop();        for(int c=0;c<26;c++){            if(ch[u][c]){                int v=ch[u][c];                for(f=fail[u];f && !ch[f][c];f=fail[f]);                if(f) fail[v]=ch[f][c]; else fail[v]=1;                q.push(v);            }            else{                ch[u][c]=ch[fail[u]][c]?ch[fail[u]][c]:1;            }        }    }}struct Edge{    int to,next;} e[N*2];int head[N], ec;void add(int a,int b){//  D(a); D(b); E;    ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;}void dfs(int u,int f){    pa[u][0]=f; dep[u]=dep[f]+1; pos[u]=++tim;    for(int i=head[u];i;i=e[i].next){        int v=e[i].to;        if(v==f) continue;        dfs(v,u);    }    end[u]=tim;}void initLCA(){    for(int j=1;j<=21;j++){        for(int i=1;i<=sz;i++){            pa[i][j]=pa[pa[i][j-1]][j-1];        }    }}int LCA(int a,int b){    if(dep[a]<dep[b]) swap(a,b);    int cha=dep[a]-dep[b];    for(int j=21;j>=0;j--){        if(cha&(1<<j)) a=pa[a][j];    }    if(a!=b){        for(int j=21;j>=0;j--){            if(pa[a][j]!=pa[b][j]){                a=pa[a][j]; b=pa[b][j];            }        }        a=pa[a][0];    }    return a;}void failBuild(){    for(int i=2;i<=sz;i++) add(fail[i],i);    dfs(1,0); initLCA();//  for(int i=1;i<=sz;i++) D(i), D(pos[i]), E;}struct BIT{    int c[N], sz;    void init(int _sz){ sz=_sz; memset(c,0,sizeof(c)); }    void add(int x,int d){ while(x<=sz){ c[x]+=d; x+=x&(-x); } }    int sum(int x){ int ans=0; while(x){ans+=c[x];x-=x&(-x);} return ans; }    int sum(int x,int y){ return sum(y)-sum(x-1); }} bit;bool cmp(int a,int b){    return pos[a]<pos[b];}void work(char s[]){    int x=1; int tpsz=0; tp[tpsz++]=1;    for(int i=0;s[i]!='\0';i++){        x=ch[x][s[i]-'a'];        tp[tpsz++]=x;    }    sort(tp,tp+tpsz,cmp);     tpsz=unique(tp,tp+tpsz)-tp;//  D(tpsz); E;//  for(int i=0;i<tpsz;i++){ D(tp[i]); E; }    for(int i=0;i<tpsz;i++){        bit.add(pos[tp[i]],1);        if(i) bit.add(pos[LCA(tp[i-1],tp[i])],-1);    }}void solve(){    bit.init(sz);    cin>>q; int op,p;    while(q--){//      D(q); E;        scanf("%d",&op);        if(op==1){            scanf("%s",str);            work(str);        }        else{            scanf("%d",&p);            int l=pos[ptr[p]], r=end[ptr[p]];//          D(l); D(r); E;            printf("%d\n",bit.sum(l,r));        }    }}int main(){    freopen("a.in","r",stdin);    cin>>n;    for(int i=1;i<=n;i++){        scanf("%s",str);        ptr[i]=insert(str);    }//  D(sz); E;    acBuild();//  for(int i=1;i<=sz;i++) D(i), D(fail[i]), E;    failBuild();    solve();}