http://www.elijahqi.win/archives/2910
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
Sample Output
1
2
1
HINT
【数据范围】
1 <= n,q <= 100000;
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;
字符串都由小写英文字母组成。
Source
鸣谢 Dzy
首先将alice的集合建AC自动机 那么每次添加bob的集合的时候可以知道去ac自动机上跑一下 每个经过的节点的fail树上的那条链都需要添加一下计数器 但是这样暴力跳fail链复杂度是不对的大概最坏会是n^2的复杂度 那么相当于我需要计算链的这个并 那么就有一个神奇的做法 首先把我这次这个单词要经过的AC自动机上所有点按照dfs序排序 那么给每个点都+1 然后统计的时候直接统计该单词子树里+1的个数即可 但是可能会有重复的所以需要把lca处都-1 然后即可
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000010
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
int cnt=1,num,trans[N][26],ed[N],in[N],out[N],fail[N],dep[N],fa[N][20],Log[N];
int n,S[N],h[N];char s[N];
struct node{
int y,next;
}data[N];
inline void insert1(int x,int y){
data[++num].y=y;data[num].next=h[x];h[x]=num;
}
inline void buildtr(char *s,const int &id){
int len=strlen(s+1),p=1;
for (int i=1,nxt;i<=len;++i){
if (!trans[p][s[i]-'a']) trans[p][s[i]-'a']=nxt=++cnt;
else nxt=trans[p][s[i]-'a'];p=nxt;
}ed[id]=p;
}
inline void buildAC(){
queue<int>q;q.push(1);for (int i=0;i<26;++i) trans[0][i]=1;
while(!q.empty()){
int x=q.front();q.pop();
for (int i=0;i<26;++i){
int &y=trans[x][i];
if (y) fail[y]=trans[fail[x]][i],q.push(y);
else {y=trans[fail[x]][i];continue;}insert1(fail[y],y);
}
}
}
inline int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);int dis=dep[x]-dep[y];
for (int i=0;i<=Log[dis];++i) if (dis&(1<<i)) x=fa[x][i];
if(x==y) return x;
for (int i=Log[dep[y]];~i;--i)
if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline void add(int x,int v){for (register int i=x;i<=cnt;i+=i&-i) S[i]+=v;}
inline int query(int x){int tmp=0;while(x) tmp+=S[x],x-=x&-x;return tmp;}
inline void dfs(int x){
in[x]=++num;//printf("%d\n",x);
for (int i=h[x];i;i=data[i].next){
int y=data[i].y;if (y==fa[x][0]) continue;fa[y][0]=x;
dep[y]=dep[x]+1;for (int j=1;j<=Log[dep[y]];++j) fa[y][j]=fa[fa[y][j-1]][j-1];dfs(y);
}out[x]=num;
}int q[N];
inline bool cmp(const int &a,const int &b){return in[a]<in[b];}
int main(){
freopen("bzoj3881.in","r",stdin);
n=read();for (int i=1;i<=n;++i) scanf("%s",s+1),buildtr(s,i);
buildAC();Log[0]=-1;for (int i=1;i<=cnt;++i) Log[i]=Log[i>>1]+1;
num=0;dfs(1);int m=read();
for (int i=1;i<=m;++i){
int op=read();if (op==1){scanf("%s",s+1);static int len,p,nm;
p=1;len=strlen(s+1);nm=0;
for (int j=1;j<=len;++j) p=trans[p][s[j]-'a'],q[++nm]=p;
sort(q+1,q+nm+1,cmp);
for (int j=1;j<nm;++j) add(in[lca(q[j],q[j+1])],-1);
for (int j=1;j<=nm;++j) add(in[q[j]],1);continue;
}static int id;id=read();W(query(out[ed[id]])-query(in[ed[id]]-1));write_char('\n');
}flush();
return 0;
}