原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3881
题意:
Alice有n个字符串 S1,S2...Sn S 1 , S 2 . . . S n ,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串 Sx S x 。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。
数据范围
1 <= n,q <= 100000
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000
字符串都由小写英文字母组成。
好题。想到对S建AC自动机就挺妙的。
A包含串B多少次,就是A在AC自动机上的每个节点,有多少在B结尾节点的fail树子树中。
那么:
1操作相当于把P的这些节点染上一种新的颜色(颜色不覆盖可并存)
2操作相当于查询 Sx S x 结尾节点fail树子树颜色种类。
就是经典的按dfs序排序,u++,v- -,lca(u,v)- -的操作,每次查询子树权值和,用树状数组维护。
并不需要把P也添加进AC自动机,最后还得对应到S的节点上,直接在S集合构建的AC自动机上面跑,把经过的串操作了即可。
倍增lca会被卡,写树链剖分或LCT。
代码:
#include
#include
#include
#include
#include
using namespace std;
const int N=2000003;
queue<int> Q;
int n,q,ch[N][26],fail[N],root=1,tail=1,S[N],cnt=0,C[N];
int head[N],to[N],nxt[N],num=0,pos[N],size[N],son[N],top[N],in[N],out[N],inc=0,dep[N],fa[N];
char str[N];
bool cmp(const int &A,const int &B){return in[A]void ADD(int x,int d) {for(int i=x;i<=inc;i=i+(i&(-i))) C[i]+=d;}
inline int query(int x){int ret=0; for(int i=x;i;i-=(i&(-i))) ret+=C[i]; return ret;}
void build(int u,int v)
{
num++;
to[num]=v;
nxt[num]=head[u];
head[u]=num;
}
inline int insert()
{
int len=strlen(str); int tmp=root;
for(int i=0;iint c=str[i]-'a';
if(!ch[tmp][c]) ch[tmp][c]=++tail;
tmp=ch[tmp][c];
}
return tmp;
}
void getfail()
{
for(int i=0;i<26;i++) if(ch[root][i]) fail[ch[root][i]]=root,build(root,ch[root][i]),Q.push(ch[root][i]); else ch[root][i]=root;
while(!Q.empty())
{
int top=Q.front(); Q.pop();
for(int i=0;i<26;i++)
{
if(!ch[top][i]) ch[top][i]=ch[fail[top]][i];
else
{
int u=ch[top][i];
fail[u]=ch[fail[top]][i];
build(fail[u],u);
Q.push(u);
}
}
}
}
void dfs1(int u,int f)
{
size[u]=1; dep[u]=dep[f]+1; fa[u]=f;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
dfs1(v,u); size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp; inc++; in[u]=inc;
if(son[u]) dfs2(son[u],tp);
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==son[u]) continue;
dfs2(v,v);
}
out[u]=inc;
}
int getlca(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]return dep[u]void add()
{
int len=strlen(str); int tmp=root; cnt=0;
for(int i=0;iint c=str[i]-'a';
tmp=ch[tmp][c];
S[++cnt]=tmp;
}
sort(S+1,S+cnt+1,cmp); int last=0;
for(int i=1;i<=cnt;i++)
{
if(S[i]==last) continue;
ADD(in[S[i]],1);
if(last!=0) ADD(in[getlca(last,S[i])],-1);
last=S[i];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",str);
pos[i]=insert();
}
getfail();
dfs1(1,1); dfs2(1,1);
scanf("%d",&q);
while(q--)
{
int opt,x;
scanf("%d",&opt);
if(opt==1) scanf("%s",str),add();
else
{
scanf("%d",&x);
printf("%d\n",query(out[pos[x]])-query(in[pos[x]]-1));
}
}
return 0;
}