CF1207G Indie Album(AC自动机+线段树维护DFS序)

有n次操作,格式为1 c或2 j c,分别表示新建一个为c的字符串,和在第j次操作得到的串后接上c后新建一个字符串。

接着是m次询问,每次询问版本i的串中,t的出现次数。

m次询问,每次询问版本i的字符串中,t的出现次数。

如果暴力建字符串肯定存不下。

考虑a aa aaa aaaa aaaaa...这种情况,必爆。

现在考虑优化操作2。

称每种操作2所修改的字符串编号为它的父亲。

每个由操作2得到的字符串的父亲节点唯一。

那么可以先对所有字符串建一颗字符树。

然后把询问离线,即每种字符串i对应的询问集中处理。

然后,先对所有t串建AC自动机。

然后在字符树上搜索,维护当前节点到根节点的路径。

为了方便操作,加一个巨大的根节点,编号为0。

往下搜,就把对应下一个节点的AC自动机上的节点编号塞入路径。

回溯的时候,把路径的最后一个节点编号退出即可。

然后,对当前路径,依次处理询问。

加入路径的时候,在fail树上让对应点权+1。

处理单个询问的t串的时候,询问子树和就可以。

这样就可以用线段树维护fail树的dfs序来处理子树和。

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+100;
vector<int> gg[maxn];
char a[maxn];
string t[maxn];
vector<int> qq[maxn];
int n,m,q;
int tr[maxn][26],fail[maxn],sz[maxn],tot;
vector<int> g[maxn];
int ed[maxn];
int dfn[maxn],cnt;
int insert (string s) {
	int u=0;
	for (char i:s) {
		if (!tr[u][i-‘a‘]) tr[u][i-‘a‘]=++tot;
		u=tr[u][i-‘a‘];
	}
	return 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 c[maxn<<2];
void up (int i,int l,int r,int p,int v) {
	if (l==p&&r==p) {
		c[i]+=v;
		return;
	}
	int mid=(l+r)>>1;
	if (p<=mid) up(i<<1,l,mid,p,v);
	if (p>mid) up(i<<1|1,mid+1,r,p,v);
	c[i]=c[i<<1]+c[i<<1|1];
}
int query (int i,int l,int r,int L,int R) {
	if (l>=L&&r<=R) return c[i];
	int mid=(l+r)>>1;
	int ans=0;
	if (L<=mid) ans+=query(i<<1,l,mid,L,R);
	if (R>mid) ans+=query(i<<1|1,mid+1,r,L,R);
	return ans; 
}
void dfs1 (int u) {
	sz[u]=1;
	dfn[u]=++cnt;
	for (int v:g[u]) {
		dfs1(v);
		sz[u]+=sz[v]; 
	}
}
int ans[maxn];
void dfs2 (int u,int uu) {
	for (int v:qq[u]) ans[v]=query(1,1,cnt,dfn[ed[v]],dfn[ed[v]]+sz[ed[v]]-1);
	for (int v:gg[u]) {
		up(1,1,cnt,dfn[tr[uu][a[v]-‘a‘]],1);
		dfs2(v,tr[uu][a[v]-‘a‘]);
		up(1,1,cnt,dfn[tr[uu][a[v]-‘a‘]],-1);
	}	
}
int main () {
	ios::sync_with_stdio(false);
	cin>>n;
	while (n--) {
		int op;
		cin>>op;
		if (op==1) {
			m++;
			gg[0].push_back(m);
			char ch;
			cin>>ch;
			a[m]=ch;
		}
		else {
			int x;
			char ch;
			cin>>x>>ch;
			m++;
			gg[x].push_back(m);
			a[m]=ch;
		}
	}
	cin>>q;
	for (int i=1;i<=q;i++) {
		int x;
		cin>>x>>t[i];
		qq[x].push_back(i);
		ed[i]=insert(t[i]);
	}
	build();
	for (int i=1;i<=tot;i++) g[fail[i]].push_back(i);
	dfs1(0);
	dfs2(0,0);
	for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
	
}

CF1207G Indie Album(AC自动机+线段树维护DFS序)

上一篇:使用Go env命令设置Go的环境


下一篇:顺序表的操作——删除