CF547E Mike and Friends(AC自动机的fail树dfs序上建立可持久化线段树)
给定n个字符串s1,s2,...sn
q次询问,第k个字符串在s[l,r]中出现了多少次。
首先肯定是对所有的字符串建立AC自动机。
然后,考虑弱化版本,第k个字符串在所有字符串中出现了多少次。
先找到第k个字符串在字典树内对应的节点。
然后,k的整个子树所表示的字符串,k都是它们的后缀。
这样就可以先遍历每个字符串,它每个前缀对应的字典图节点点权+1。
然后对k的整个子树,子树的点权和就是k在所有字符串中的出现次数。
然后,加上询问条件[l,r]。
即询问一个子树区间内编号在[l,r]之间的点权之和。
可持久化线段树维护fail树的DFS序,然后处理每个询问即可。
然后考虑每个模式串不相同的情况,一个子树节点可能对应多个编号,同时在可持久化线段树里更新即可。
之前学可持久化的例题,现在终于悟了...
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
string s[maxn];
vector<int> g[maxn];
vector<int> gg[maxn];
int tr[maxn][26],sz[maxn],fail[maxn],tot;
int n,q;
int ed[maxn];
void insert (string s,int x) {
int u=0;
for (char i:s) {
if (!tr[u][i-'a']) tr[u][i-'a']=++tot;
u=tr[u][i-'a'];
gg[u].push_back(x);
}
ed[x]=u;
}
void build () {
queue<int> q;
for (int i=0;i<26;i++) {
if (tr[0][i]) {
q.push(tr[0][i]);
}
}
while (q.size()) {
int u=q.front();
q.pop();
for (int i=0;i<26;i++) {
if (tr[u][i]) {
fail[tr[u][i]]=tr[fail[u]][i];
q.push(tr[u][i]);
}
else {
tr[u][i]=tr[fail[u]][i];
}
}
}
}
int dfn[maxn],id[maxn],cnt;
void dfs (int u) {
sz[u]=1;
dfn[u]=++cnt;
id[cnt]=u;
for (int v:g[u]) {
dfs(v);
sz[u]+=sz[v];
}
}
const int M=maxn*32;
int c[M],tol,lson[M],rson[M],T[maxn];
int build (int l,int r) {
int rt=++tol;
c[rt]=0;
if (l==r) return rt;
int mid=(l+r)>>1;
lson[rt]=build(l,mid);
rson[rt]=build(mid+1,r);
return rt;
}
int up (int root,int l,int r,int p,int v) {
int newRoot=++tol;
if (l==r) {
c[newRoot]=c[root]+v;
return newRoot;
}
int mid=(l+r)>>1;
if (p<=mid) {
lson[newRoot]=up(lson[root],l,mid,p,v);
rson[newRoot]=rson[root];
}
else {
rson[newRoot]=up(rson[root],mid+1,r,p,v);
lson[newRoot]=lson[root];
}
c[newRoot]=c[lson[newRoot]]+c[rson[newRoot]];
return newRoot;
}
int query (int lr,int rr,int l,int r,int L,int R) {
if (l>=L&&r<=R) return c[rr]-c[lr];
int mid=(l+r)>>1;
int ans=0;
if (L<=mid) ans+=query(lson[lr],lson[rr],l,mid,L,R);
if (R>mid) ans+=query(rson[lr],rson[rr],mid+1,r,L,R);
return ans;
}
int main () {
ios::sync_with_stdio(false);
cin>>n>>q;
for (int i=1;i<=n;i++) {
cin>>s[i];
insert(s[i],i);
}
build();
for (int i=1;i<=tot;i++) g[fail[i]].push_back(i);
dfs(0);
T[0]=build(1,n);
for (int i=1;i<=cnt;i++) {
int x=id[i];
int tt=T[i-1];
for (int j:gg[x]) {
tt=up(tt,1,n,j,1);
}
T[i]=tt;
}
//printf("%d\n",query(T[0],T[cnt],1,n,1,n));
while (q--) {
int k,l,r;
cin>>l>>r>>k;
int ans=query(T[dfn[ed[k]]-1],T[dfn[ed[k]]+sz[ed[k]]-1],1,n,l,r);
//int ans=0;
//for (int i=dfn[ed[k]];i<=dfn[ed[k]]+sz[ed[k]]-1;i++) for (int j:gg[id[i]]) if (j>=l&&j<=r) ans++;
printf("%d\n",ans);
}
}