(Large atural) CF1207G Indie Album 原题链接
解法
这种题算是比较套路的了,我们看见这种多字符串匹配,会想到AC自动机。
如果不会AC自动机的可以看看 我的博客 ,希望能给您带来帮助。
那么如果我们对于所有字典树节点连边 (i-fail_i)(根节点没有 (fail),不连边),那么就得到了一棵 Fail树。
那么,如果一号点为根节点,那所有 (fail) 直接或间接指向 (i) 号点的节点,都在 (i) 的子树中。
所以查询字符串 (X) 在字符串 (Y) 中出现几次,等价于建出 Trie 和 Fail树 后,在 Fail树中 以 “(X)的结束节点”(设为 (i)) 为根的子树中有多少个 (Y) 包含的节点。
不理解可以看这解释:比如 (j) 号点是 (Y) 所包含的,是 (Y) 的第 (id) 个节点,那么代表在 (Y) 查询时,(j) 跑 (fail) 可以跑出 (X),所以 (X) 是 (Y) 中 (1sim id) 这个子串的后缀。
对于这道题,我们可以类比 阿机的打字狸 :
设询问的字符串为 (T),则显然有这样一个方法:让 (T) 在 Trie 上跑,经过的点对于的 Fail树节点权值加一。然后我们查询 (S) 在 Trie 的结尾节点 (G),对应的 Fail树节点 ,的子树和(可能有点绕)。最后我们再把那些加一的点再减一,复原原来的树。
把答案离线后,我们可以在 Trie树 上 DFS,来到一个点,这个点对于的 Fail树节点 权值加一;离开一个点,同样地权值减一。这样当我们到达某个字符串的末尾时,我们可以保证有且仅有这个字符串的点上有值。然后我们调出在这个点上的每一个询问,查询它们对应的 Fail树节点 的子树和即可。
所以我们需要支持单点加值,以及查询一个点的子树和,可以用 DFS序 解决。
因为一个点的子树在 DFS序 上是连续的,所以我们只用区间查询(给子树查询)、单点加值( 在 Trie 跑上 DFS 时加减)就行了,显然套树状数组模板即可。
代码
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define mar(o) for(int E=fst[o];E;E=e[E].nxt)
#define v e[E].to
using namespace std;
const int n7=401234,m7=801234,z7=1601234;
struct dino{int to,nxt;}e[z7];
struct lyca{int z,id;};
int n,T,cnt=1,tre[m7][26],fail[m7],poi[n7],head,tail,que[m7];
int ecnt,fst[m7],t,atre[n7],L[n7],R[n7],ans[n7];
char cr[n7];bool tru[m7][26];
vector <lyca> vec[m7];
int rd(){
int shu=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
return shu;
}
void edge(int sta,int edn){
ecnt++;
e[ecnt]=(dino){edn,fst[sta]};
fst[sta]=ecnt;
}
void insert1(){
rep(i,1,n){
int sys=rd(),las=1;
if(sys==2)las=poi[ rd() ];
char ch=getchar()-'a';
if(!tre[las][ch]){
cnt++,tre[las][ch]=cnt;
}
poi[i]=tre[las][ch];
}
}
void insert2(int z){
int len=strlen(cr+1),now=1;
rep(i,1,len){
int ch=cr[i]-'a';
if(!tre[now][ch]){
cnt++,tre[now][ch]=cnt;
}
now=tre[now][ch];
}
poi[z]=now;
}
void Gfail(){
head=1,tail=1,que[1]=1;
rep(i,0,25)tre[0][i]=1;
while(head<=tail){
int now=que[head];
rep(i,0,25){
int edn=tre[ fail[now] ][i];
if(tre[now][i]){
fail[ tre[now][i] ]=edn;
edge(edn,tre[now][i]);
tail++,que[tail]=tre[now][i];
tru[now][i]=1;
}
else tre[now][i]=edn;
}
head++;
}
}
#define lb(z) (z&-z)
void updat(int z,int id){
while(id<=cnt)atre[id]+=z,id+=lb(id);
}
int Dquery(int id){
int tot=0;
while(id)tot+=atre[id],id-=lb(id);
return tot;
}
int query(int l,int r){
return Dquery(r)-Dquery(l-1);
}
void dfs1(int o){
t++,L[o]=t;
mar(o)dfs1(v);
R[o]=t;
}
int fimd(){
int len=strlen(cr+1),now=1;
rep(i,1,len)now=tre[now][ cr[i]-'a' ];
return now;
}
void dfs2(int o){
updat(1,L[o]);
int wal=vec[o].size()-1;
rep(i,0,wal){
int ll=L[ vec[o][i].z ];
int rr=R[ vec[o][i].z ];
ans[ vec[o][i].id ]=query(ll,rr);
}
rep(i,0,25){
if(tru[o][i])dfs2(tre[o][i]);
}
updat(-1,L[o]);
}
int main(){
n=rd(),insert1(),T=rd();
rep(i,1,T){
int z=rd();scanf("%s",cr+1);
insert2(i+n);
vec[ poi[z] ].push_back( (lyca){poi[i+n],i} );
}
Gfail(),dfs1(1),dfs2(1);
rep(i,1,T)printf("%d
",ans[i]);
return 0;
}