BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组

BZOJ_3881_[Coci2015]Divljak_AC自动机+dfs序+树状数组

Description

Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。

Input

第1行,一个数n;
接下来n行,每行一个字符串表示S_i;
下一行,一个数q;
接下来q行,每行一个操作,格式见题目描述。

Output

对于每一个Alice的询问,帮Bob输出答案。

Sample Input

3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3

Sample Output

1
2
1

HINT

【数据范围】
1 <= n,q <= 100000;
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;

对S集中的串建立AC自动机。
首先我们要知道fail树子树中有多少个结点就代表它是多少个串的后缀。
当我们插入一个串时,沿着AC自动机走,将所有点+1,然后子树和就代表有多少包含这个串。
但题中要求的是集合T中有多少字符串满足条件,因此我们需要去重。
即求一些根到点路径的并。
把点按dfs序排序,然后加入每个点的贡献,减去lca的贡献。
 
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline char nc() {
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int rd() {
    int x=0;char c=nc();
    while(c<'0'||c>'9') c=nc();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=nc();
    return x;
}
#define N 2000050
#define M 100050
int fail[N],ch[N][26],xx[M],yy[M],n,idx,cnt=1,pos[M],Q[N],l,r,head[N],to[N],nxt[N],c[N],dfn[N],son[N],a[N],m;
int f[22][N],dep[N];
bool cmp(int x,int y) {
    return dfn[x]<dfn[y];
}
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void fix(int x,int v) {
    for(;x<=dfn[0];x+=x&(-x)) c[x]+=v;
}
int inq(int x) {
    int re=0; 
    for(;x;x-=x&(-x)) re+=c[x];
    return re;
}
void build_ac() {
    int i,p;
    for(i=0;i<26;i++) ch[0][i]=1;
    Q[r++]=1;
    while(l<r) {
        p=Q[l++];
        for(i=0;i<26;i++) {
            if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i];
            else ch[p][i]=ch[fail[p]][i];
        }
    }
}
void dfs(int x) {
    int i; 
    dfn[x]=++dfn[0];
    for(i=head[x];i;i=nxt[i]) {
        f[0][to[i]]=x; dep[to[i]]=dep[x]+1;
        dfs(to[i]);
    }
    son[x]=dfn[0];
}
int lca(int x,int y) {
    int i;
    if(dep[x]<dep[y]) swap(x,y);
    for(i=21;i>=0;i--) {
        if(dep[f[i][x]]>=dep[y]) x=f[i][x];
    }
    if(x==y) return x;
    for(i=21;i>=0;i--) {
        if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
    }
    return f[0][x];
}
int main() {
    n=rd();
    int i,j,x;
    for(i=1;i<=n;i++) {
        char s=nc(); int p=1;
        while(s<'a'||s>'z') s=nc();
        for(;s>='a'&&s<='z';s=nc()) {
            int &k=ch[p][s-'a'];
            if(!k) k=++cnt;
            p=k;
        }
        pos[i]=p;
    }
    int tot=cnt; cnt=0;
    build_ac();
    for(i=1;i<=tot;i++) if(fail[i]) add(fail[i],i);
    dfs(1);
    for(i=1;(1<<i)<=tot;i++) {
        for(j=1;j<=tot;j++) {
            f[i][j]=f[i-1][f[i-1][j]];
        }
    }
    m=rd();
    int opt;
    while(m--) {
        opt=rd();
        if(opt==1) {
            a[0]=0;
            char s=nc(); int p=1;
            while(s<'a'||s>'z') s=nc();
            for(;s>='a'&&s<='z';s=nc()) {
                p=ch[p][s-'a'];
                a[++a[0]]=p; fix(dfn[p],1);
            }
            sort(a+1,a+a[0]+1,cmp);
            for(i=1;i<a[0];i++) {
                fix(dfn[lca(a[i],a[i+1])],-1);
            }
        }else {
            x=rd();
            int p=pos[x];
            printf("%d\n",inq(son[p])-inq(dfn[p]-1));
        }
    }
}

 

posted @ 2018-06-15 13:12  fcwww  阅读(151)  评论(0编辑  收藏  举报