CF1207G Indie Album
首先,如果直接暴力存那 \(n\) 个文本串空间最大 \(n^2\) 级别显然不行。
我一开始以为是“可持久化AC自动机”,想了好久甚至用LCT去维护。那个翻译写“版本”真是害人不浅qwq
后来发现那就是一颗标准的 \(Trie\) 树,读入的时候建 \(Trie\) 即可存下所有的串。
关于查询,在线不是很好搞,那就离线下来,把询问挂到 \(Trie\) 上。
看看现在需要干什么:求出当前 \(Trie\) 节点所代表的字符串中 \(t\) 出现了几次。
这种问题一般都是把询问串建 \(ACAM\) ,然后跑 \(dp\) 维护子树和。
动态就是 CF163E e-Government ,搞个树状数组以及 \(fail\) 树的 \(dfs\) 序就能做,因为我们只需要知道子树内出现的状态数,所以是一个单点修改区间求和的问题。
那么我们把模式串建 \(ACAM\) ,跑出 \(fail\) 树的 \(dfs\) 序。
在文本串的 \(Trie\) 上跑 \(dfs\) ,遍历所有节点回答询问。
考虑在 \(dfs\) 的时候记录两个值来表示状态:\(u1,u2\) 分别表示,在文本串 \(Trie\) 上的节点编号,在询问串上 \(Trie\) 图的节点编号。
让 \(u1\) 沿着文本串 \(Trie\) 跑,\(u2\) 沿着询问串 \(Trie\) 图跑,根据 \(u1\) 回答询问,根据 \(u2\) 来在 \(fail\) 树上修改。
对于每一个 \(dfs\) 状态, \(u2\) 所在的节点就是 \(u1\) 代表的文本串 \(Trie\) 上前缀的状态,相当于跑单个文本串。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define x first
#define y second
#define sz(v) (int)v.size()
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return f?x:-x;
}
int rdc(){
char ch=getchar();
while(!isalpha(ch))ch=getchar();
return ch-'a';
}
#define N 400005
int n,m,ch[N][26],ed[N],to[N],ln[N],rn[N],tmr,tr[N],tot,ans[N],fail[N],trie[N][26],cnt;
vector<int>e[N],q[N];
void insert(char*str,int id){
int u=0;
for(int i=0,len=strlen(str);i<len;++i){
int c=str[i]-'a';
if(!ch[u][c])ch[u][c]=++cnt;
u=ch[u][c];
}
ed[id]=u;
}
void build_fail(){
queue<int>q;
for(int i=0;i<26;++i)if(ch[0][i])q.push(ch[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i)
if(ch[u][i])fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]);
else ch[u][i]=ch[fail[u]][i];
}
for(int i=1;i<=cnt;++i)e[fail[i]].pb(i);
}
void dfs1(int u){
ln[u]=++tmr;
for(int v:e[u])dfs1(v);
rn[u]=tmr;
}
void add(int x,int d){if(!x)return;for(int i=x;i<=tmr;i+=i&-i)tr[i]+=d;}
int ask(int x){int res=0;for(int i=x;i>0;i-=i&-i)res+=tr[i];return res;}
void dfs2(int u1,int u2){
add(ln[u2],1);
for(int i:q[u1])ans[i]=ask(rn[ed[i]])-ask(ln[ed[i]]-1);
for(int i=0;i<26;++i)if(trie[u1][i])dfs2(trie[u1][i],ch[u2][i]);
add(ln[u2],-1);
}
signed main(){
n=read();
for(int i=1;i<=n;++i){
int op=read(),pre=0,x;
if(op==2)pre=read();
x=rdc();
if(!trie[to[pre]][x])trie[to[pre]][x]=++tot;
to[i]=trie[to[pre]][x];
}
m=read();
for(int i=1;i<=m;++i){
int x=read();static char str[N];
scanf("%s",str);
insert(str,i),q[to[x]].pb(i);
}
build_fail(),dfs1(0),dfs2(0,0);
for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
return 0;
}
路漫漫其修远兮,吾将上下而求索