题意:
有 n n n次操作,对于第 i i i个操作由下列两种方式的一种给出
- 1 1 1 c c c ,该字符串只含一个字符 c c c
-
2
2
2
x
x
x
c
c
c,该字符串为第
x
(
1
≤
x
≤
i
)
x(1≤x≤i)
x(1≤x≤i)个字符串末尾添加一个字符
c
c
c得到
然后有Q次询问,每次询问给出一个字符串 s s s和位置编号 x x x,问在第 x x x个字符串中,字符串 s s s出现了几次
题解:
A C AC AC自动机,树状数组/线段树, d f s dfs dfs序
首先我们可以将询问变成离线,对于所有询问的字符串建立AC自动机,同时建出fail树,然后利用fail树的性质:一个字符串在母串中出现的次数为母串在AC自动机上跑一遍并将走到的位置权值+1,该字符串所对应的fail结点的子树权值和。
现在我们的思路就比较清晰了:
在建出的AC自动机进行dfs,每走到一个结点,将所有以S为后缀的字符串的出现次数+1
如果经过的这个点是我们要求的字符串的尾结点,那么我们直接统计子树和即可
记得回溯的时候出现次数-1
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5+50;
const int INF = 0x3f3f3f3f;
int nxt[MAXN][26],fail[MAXN],fa[MAXN];
char str[MAXN],ss[MAXN];
int head[MAXN],res[MAXN];
vector<pair<int,int> > ans[MAXN];
vector<int> g[MAXN];
struct node{
int v,nxt;
}edge[MAXN<<1];
int tot,num,itot;
int sz[MAXN],dfn[MAXN],tree[MAXN];
inline void add(int u,int v){
edge[++tot].v = v,edge[tot].nxt = head[u],head[u] = tot;
}
inline int Insert(char *s){
int root=0,len=strlen(s);
for(int i=0;i<len;i++){
if(!nxt[root][s[i]-'a']) nxt[root][s[i]-'a'] = ++itot;
root = nxt[root][s[i]-'a'];
}
return root;
}
inline void Build(){
queue<int> que;
for(int i=0;i<26;i++)
if(nxt[0][i])
que.push(nxt[0][i]);
while(!que.empty()){
int u = que.front(); que.pop();
g[fail[u]].push_back(u);
for(int i=0;i<26;i++){
if(nxt[u][i]) fail[nxt[u][i]] = nxt[fail[u]][i],que.push(nxt[u][i]);
else nxt[u][i] = nxt[fail[u]][i];
}
}
}
void dfs(int u){
dfn[u]=++num,sz[u]=1;
for(int i=0;i<(int)g[u].size();i++){
int v = g[u][i];
dfs(v);
sz[u] += sz[v];
}
}
inline int lowbit(int x) { return x&-x; }
inline void update(int x,int val){
for(int i=x;i<=num;i+=lowbit(i)) tree[i] += val;
}
inline int query(int x){
int sum = 0;
for(int i=x;i;i-=lowbit(i)) sum += tree[i];
return sum;
}
void dfs(int u,int p){
p = nxt[p][str[u]-'a'];
update(dfn[p],1);
for(int i=head[u];i;i=edge[i].nxt){
int v = edge[i].v;
dfs(v,p);
}
for(int i=0;i<(int)ans[u].size();i++){
int x = ans[u][i].first,id = ans[u][i].second;
res[id] = query(dfn[x]+sz[x]-1)-query(dfn[x]-1);
}
update(dfn[p],-1);
}
int main(){
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
int op,k; scanf("%d",&op);
if(op==2) scanf("%d",&k),fa[i]=k;
add(fa[i],i);
scanf("%s",&str[i]);
}
int m; scanf("%d",&m);
for(int i=1;i<=m;i++){
int id; scanf("%d%s",&id,ss);
int k = Insert(ss);
ans[id].push_back(make_pair(k,i));
}
Build();
dfs(0);
for(int i=1;i<=n;i++)
if(!fa[i])
dfs(i,0);
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return 0;
}