BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)
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;
字符串都由小写英文字母组成。
之前就看过此题,但是不会树链求并,就搁着了。现在回来看还是可以做的。
Alice在字符串集合为S,Bob的字符串集合为T。先对Alice的集合建立AC自动机,得到Fail树。对于T每次新加的字符串Str,Str在自动机上跑。能跑到的所有 位置,在Fail树上对应的位置到根的所有点都要加1,重复的不重复加,就涉及到去重,即用树链求并。
把跑到是所有位置对应的Fail树位置按DFS序排序,每一个位置+1,相邻的LCA位置处-1。那么求T中有几个串含有某Si时,即是求Si在Fail树上的子树值之和。这里倍增求LCA会超时。
这个人的代码风格和我一模一样哎(但是他的AC自动机写丑了)!!!因为是权限题,自己写一遍怕错了没法知晓,就假设我写了,去找个差不多的题写写,233。
#include<bits/stdc++.h> using namespace std; #define N 2002010 int ch[N][26],fail[N],ins[N],cnt; char s[N]; int head[N],next[N],end[N]; inline void addedge(int a,int b){ static int q=1;end[q]=b,next[q]=head[a],head[a]=q++; } int in[N],out[N],dep[N],tclock; void dfs(int x){ in[x]=++tclock; for(int j=head[x];j;j=next[j]) dep[end[j]]=dep[x]+1,dfs(end[j]); out[x]=tclock; } namespace Lca_system{ int Seq[N<<1],a[8383608],M,ins[N],cnt; inline void build_dfs(int x){ ins[x]=++cnt; Seq[cnt]=x; for(int j=head[x];j;j=next[j]){ build_dfs(end[j]); Seq[++cnt]=x; } } inline int Min(int x,int y){ if(x==-1||y==-1)return x==-1?y:x; return dep[x]<dep[y]?x:y; } inline void init(){ build_dfs(0); for(M=1;M<cnt+2;M<<=1); memset(a,-1,sizeof a); for(int i=1;i<=cnt;++i)a[M+i]=Seq[i]; for(int i=M-1;i>=1;--i)a[i]=Min(a[2*i],a[2*i+1]); } inline int ask(int tl,int tr){ int re=-1; for(tl+=M-1,tr+=M+1;tl^tr^1;tl>>=1,tr>>=1){ if(~tl&1)re=Min(re,a[tl^1]); if(tr&1)re=Min(re,a[tr^1]); } return re; } inline int lca(int x,int y){ x=ins[x],y=ins[y]; if(x>y)swap(x,y); return ask(x,y); } } int seq[N],num; int A[N]; inline void modify(int x,int c){ for(;x<=cnt+1;x+=x&-x)A[x]+=c; } inline int ask(int x){ int re=0;for(;x;x-=x&-x)re+=A[x];return re; } inline bool cmp(const int&x,const int&y){return in[x]<in[y];} inline int git(){ int c,re; while(!isdigit(c=getchar())); re=c-'0'; while(isdigit(c=getchar()))re=(re<<1)+(re<<3)+c-'0'; return re; } char buf[100010*6],*o=buf; inline void print(int x){ static int s[100];int top=0; if(!x)*o++=48;else{for(;x;x/=10)s[++top]=x%10;for(int i=top;i>=1;--i)*o++=48+s[i];} *o++='\n'; } int main(){ int n=git(); register int i,j; int len,p; for(i=1;i<=n;++i){ scanf("%s",s); len=strlen(s),p=0; for(j=0;j<len;++j){ if(!ch[p][s[j]-'a'])ch[p][s[j]-'a']=++cnt; p=ch[p][s[j]-'a']; } ins[i]=p; } queue<int>q; for(i=0;i<26;++i) if(ch[0][i]) q.push(ch[0][i]); int u,v,r; while(!q.empty()){ u=q.front(),q.pop(); for(i=0;i<26;++i)if((v=ch[u][i])){ q.push(v); for(r=fail[u];r&&!ch[r][i];r=fail[r]); fail[v]=ch[r][i]; } } for(i=1;i<=cnt;++i) addedge(fail[i],i); dep[0]=1,dfs(0); Lca_system::init(); int Q=git(); int qte,x; while(Q--){ qte=git(); if(qte==1){ scanf("%s",s); len=strlen(s),p=0,num=0; for(i=0;i<len;++i){ while(p&&!ch[p][s[i]-'a'])p=fail[p]; p=ch[p][s[i]-'a']; if(p) seq[++num]=p; } sort(seq+1,seq+num+1,cmp); for(i=1;i<=num;++i)modify(in[seq[i]],1); for(i=1;i<num;++i)modify(in[Lca_system::lca(seq[i],seq[i+1])],-1); } else{ x=git(); print(ask(out[ins[x]])-ask(in[ins[x]]-1)); } } fwrite(buf,1,o-buf,stdout); return 0; }
It is your time to fight!