题目:BZOJ3881.
题目大意:Alice有
n
n
n个字符串
S
1
,
S
2
.
.
.
S
n
S_1,S_2...S_n
S1,S2...Sn,Bob有一个字符串集合
T
T
T,一开始集合为空.有
q
q
q个操作,操作有两种形式:
1
 
P
1\,P
1P,Bob往自己的集合里添加了一个字符串
P
P
P.
2
 
x
2\,x
2x,Alice询问Bob,集合
T
T
T中有多少个字符串包含串
S
x
S_x
Sx.
(原题太精简了我不知道怎么样继续精简了)
1
≤
n
,
q
≤
1
0
5
,
1
≤
∑
∣
S
i
∣
,
∑
∣
T
i
∣
≤
2
∗
1
0
6
1\leq n,q\leq 10^5,1\leq \sum |S_i|,\sum |T_i|\leq 2*10^6
1≤n,q≤105,1≤∑∣Si∣,∑∣Ti∣≤2∗106.
首先考虑对 S S S构建出AC自动机,那么加入一个串后考虑 T T T串对AC自动机上每一个点的贡献(要么是 1 1 1要么是 0 0 0).
我们发现当 T T T串在某位置匹配上AC自动机上的一个点时,那么这个点在fail树上的所有祖先都能够被匹配上,这让我们想到把 T T T串放入AC自动机中暴力跳fail指针来染色,但明显这样做没有效率保证会TLE.
很容易发现这个做法的复杂度比较假(虽说可以证明是 O ( n n ) O(n\sqrt{n}) O(nn),证明类似BZOJ3277),但其实这个问题就是树链的并,直接用dfs序+树状数组维护一下树链的并就可以 O ( n log n ) O(n\log n) O(nlogn)了.
其实用线段树合并也可以 O ( n log n ) O(n\log n) O(nlogn).
这个时间复杂度看起来并不能过掉 2 ∗ 1 0 6 2*10^6 2∗106级别的数据,但是由于我们用到的几个带 log \log log的算法常数都很小,所以这样做还是可以过的.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
#define m(a) memset(a,0,sizeof(a))
const int N=2000000,C=26;
struct Trie{
int s[C],fail;
}tr[N+9];
int cn,h[N+9];
void Trie_build(){cn=0;}
void Trie_insert(char *c,int len,int id){
int x=0;
for (int i=1;i<=len;++i)
if (tr[x].s[c[i]-'a']) x=tr[x].s[c[i]-'a'];
else {
tr[x].s[c[i]-'a']=++cn;
x=cn;
}
h[id]=x;
}
queue<int>q;
void Fail_get(){
for (int i=0;i<C;++i)
if (tr[0].s[i]) q.push(tr[0].s[i]);
while (!q.empty()){
int x=q.front(),t;q.pop();
for (int i=0;i<C;++i)
if (tr[x].s[i]) tr[tr[x].s[i]].fail=tr[tr[x].fail].s[i],q.push(tr[x].s[i]);
else tr[x].s[i]=tr[tr[x].fail].s[i];
}
}
struct tree{
int y,next;
}e[N+9];
int lin[N+9],top;
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
void Fail_build(){
for (int i=1;i<=cn;++i)
ins(tr[i].fail,i);
}
struct node{
int deep,fa,siz,son,top,dfn,low;
}d[N+9];
void Fail_dfs1(int k,int fa){
d[k].fa=fa;
d[k].deep=d[fa].deep+1;
d[k].siz=1;
d[k].son=cn+1;
for (int i=lin[k];i;i=e[i].next){
Fail_dfs1(e[i].y,k);
d[k].siz+=d[e[i].y].siz;
if (d[d[k].son].siz<d[e[i].y].siz) d[k].son=e[i].y;
}
}
int cdfs;
void Fail_dfs2(int k,int top){
d[k].dfn=++cdfs;
d[k].top=top;
if (d[k].son^cn+1) Fail_dfs2(d[k].son,top);
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^d[k].son) Fail_dfs2(e[i].y,e[i].y);
d[k].low=cdfs;
}
int Fail_lca(int x,int y){
while (d[x].top^d[y].top)
d[d[x].top].deep>d[d[y].top].deep?x=d[d[x].top].fa:y=d[d[y].top].fa;
return d[x].deep<d[y].deep?x:y;
}
int c[N+9];
void Bit_add(int x,int v){for (;x<=cn+1;x+=x&-x) c[x]+=v;}
int Bit_query(int x){int sum=0;for (;x;x-=x&-x) sum+=c[x];return sum;}
void Fail_add(int x,int v){Bit_add(d[x].dfn,v);}
int Fail_query(int x){return Bit_query(d[x].low)-Bit_query(d[x].dfn-1);}
int tmp[N+9];
bool cmp(const int &a,const int &b){return d[a].dfn<d[b].dfn;}
void Change(char *c,int len){
int x=0;
for (int i=1;i<=len;++i){
x=tr[x].s[c[i]-'a'];
Fail_add(x,1);
tmp[i]=x;
}
sort(tmp+1,tmp+1+len,cmp);
for (int i=1;i<len;++i)
Fail_add(Fail_lca(tmp[i],tmp[i+1]),-1);
}
int n,len;
char s[N+9];
Abigail into(){
scanf("%d",&n);
Trie_build();
for (int i=1;i<=n;++i){
scanf("%s",s+1);
len=strlen(s+1);
Trie_insert(s,len,i);
}
}
Abigail work(){
Fail_get();
Fail_build();
Fail_dfs1(0,cn+1);
Fail_dfs2(0,0);
}
Abigail getans(){
int q,opt,x;
scanf("%d",&q);
for (int i=1;i<=q;++i){
scanf("%d",&opt);
if (opt==1){
scanf("%s",s+1);
len=strlen(s+1);
Change(s,len);
}else{
scanf("%d",&x);
printf("%d\n",Fail_query(h[x]));
}
}
}
int main(){
into();
work();
getans();
return 0;
}