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
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3
Sample Output
1
2
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)); } } }