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
",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
",ans);
 	}
}
原文地址:https://www.cnblogs.com/zhanglichen/p/15008597.html