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); } }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步