题面:
Alice有n个字符串S1,S2...SnS_1,S_2...S_nS1,S2...Sn,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串SxS_xSx。(我们称串A包含串B,当且仅当B是A的子串)
1 <= n,q <= 100000;
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;
题目分析:
S串是不变的,对S建立AC自动机。
把串T放到自动机中,经过的节点以及fail指针对应的那些节点的被包含次数都会+1。
但是不能重复统计。按照树剖+树状数组的做法,就是把树链合并,然后区间修改,单点查询。这样的复杂度是O(lenlog2len)O(lenlog^2len)O(lenlog2len)的,想必不能承受。
把fail指针构成的fail树建出来,一个串的被包含次数就是子树中的点被经过的次数,但是对于一个T串只能记一次。
那么把经过的点按照dfs序排序,在每个点的位置+1,相邻两点的lca处-1,查询的时候就查询子树的和(单点修改区间查询),这样可以保证对于一个被包含的串i,串T对它子树的贡献和为1。复杂度是O(lenloglen)O(lenloglen)O(lenloglen)的
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 2000005
#define maxc 26
using namespace std;
int n,m,pos[maxn],in[maxn],out[maxn],tim,seq[maxn],siz[maxn],son[maxn],top[maxn],fa[maxn],dep[maxn];
int fir[maxn],nxt[maxn],to[maxn],tot;
inline void line(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
inline bool cmp(int i,int j){return in[i]<in[j];}
struct ac_automaton{
int ch[maxn][maxc],fail[maxn],sz;
void insert(char *s,int id){
int len=strlen(s),r=0,v;
for(int i=0;i<len;i++,r=ch[r][v])
if(!ch[r][v=s[i]-'a']) ch[r][v]=++sz;
pos[id]=r;
}
int Q[maxn],head,tail;
void build(){
Q[head=tail=0]=0;
while(head<=tail){
int r=Q[head++],c;
for(int i=0;i<maxc;i++)
if(c=ch[r][i]) fail[c]=r?ch[fail[r]][i]:0,Q[++tail]=c,line(fail[c],c);
else ch[r][i]=ch[fail[r]][i];
}
}
}A;
void dfs1(int u){
in[u]=++tim;siz[u]=1,son[u]=A.sz+1;
for(int i=fir[u],v;i;i=nxt[i]){
dep[v=to[i]]=dep[u]+1,fa[v]=u;
dfs1(v);siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
out[u]=tim;
}
void dfs2(int u,int tp){
top[u]=tp;
if(son[u]!=A.sz+1) dfs2(son[u],tp);
for(int i=fir[u];i;i=nxt[i]) if(to[i]!=son[u]) dfs2(to[i],to[i]);
}
int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int arr[maxn];
void upd(int i,int d){for(;i<=tim;i+=i&-i) arr[i]+=d;}
int qsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
char s[maxn];
int main()
{
int op,x;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",s),A.insert(s,i);
A.build(); siz[A.sz+1]=0,dfs1(0),dfs2(0,0);
scanf("%d",&m);
while(m--){
scanf("%d",&op);
if(op==1){
scanf("%s",s);int len=strlen(s);
for(int i=0,r=0;i<len;i++) seq[i]=(r=A.ch[r][s[i]-'a']);
sort(seq,seq+len,cmp);
upd(in[seq[0]],1);
for(int i=1;i<len;i++) if(seq[i]!=seq[i-1])
upd(in[seq[i]],1),upd(in[LCA(seq[i-1],seq[i])],-1);
}
else scanf("%d",&x),printf("%d\n",qsum(out[pos[x]])-qsum(in[pos[x]]-1));
}
}