Loading

P2414 [NOI2011] 阿狸的打字机 - AC 自动机,欧拉序

题解

当然有广义 SAM 做法,但我用的是 AC 自动机。

\(n\) 个输出的串为 \(\langle s_1,s_2,\dots,s_n\rangle\)

题目中给出的字符串实际上就是一棵 Trie 树,对这棵 Trie 建立 ACAM。

考虑“子串”实际上就是前缀的后缀。对于任意的串 \(S\),若串 \(S\) 在 Trie 上的对应节点为 \(u\),那么 \(S\) 的所有前缀就对应着 \(u\) 的所有祖先。而在 fail 树上的一个点,它的所有出现过的后缀就是它在 fail 树上的所有祖先。

对于一个询问 \((x,y)\),我们要统计 \(s_x\)\(s_y\) 中的出现次数。设 \(s_x\) 的对应节点为 \(u\)\(s_y\) 对应 \(v\),那么也就相当于求 \(\sum_{x\in \operatorname{subtree}(u)} [x\in \operatorname{anc}(v)]\),其中 \(\operatorname{subtree}\) 表示 fail 树上某个点的子树,\(\operatorname{anc}\) 表示 Trie 树上某个点的祖先。

分析到这里,其实已经可以用可持久化线段树做了,但有一种更简单的做法:将询问离线,在对 Trie 树进行 dfs 的过程中,用树状数组记录当前点的所有祖先。对于每个当前点的询问,在树状数组上进行区间查询即可。

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T>
void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args>
void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
const int N=1e5+5,Sigma=26;
char s[N];
int n,m;
int tr[N][Sigma],fa[N],tot,ed[N],fail[N],num[N];
void Build(){
	queue<int> q;
	For(i,0,25) if(tr[0][i]) q.push(tr[0][i]);
	while(!q.empty()){
		int u=q.front();q.pop();
		For(i,0,25){
			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];
		}
	}
}
vector<int> e[N];
int dfn[N],dfx,siz[N];
void Dfs(int u){
	dfn[u]=++dfx,siz[u]=1;
	for(int v:e[u]){
		Dfs(v);
		siz[u]+=siz[v];
	}
}
vector<pair<int,int>> qry[N];
int ans[N];
struct Bit{
	int t[N];
	void Add(int p,int k){
		for(;p<=dfx;p+=p&-p) t[p]+=k;
	}
	int Qry(int p){
		int res=0;
		for(;p;p-=p&-p) res+=t[p];
		return res;
	}
}bit;
int Tr[N][Sigma];
void Dfs1(int u){
	bit.Add(dfn[u],1);
	for(auto x:qry[u]){
		int v=num[x.first];
		ans[x.second]=bit.Qry(dfn[v]+siz[v]-1)-bit.Qry(dfn[v]-1);
	}
	For(i,0,25){
		if(Tr[u][i]){
//			printf("%d(%c)->%d\n",u,i+'a',Tr[u][i]);
			Dfs1(Tr[u][i]);
		}
	}
	bit.Add(dfn[u],-1);
}
int main(){
	scanf("%s",s+1);
	int len=strlen(s+1),u=0,cnt=0;
	For(i,1,len){
		if(s[i]=='B') u=fa[u];
		else if(s[i]=='P') ++ed[u],num[++cnt]=u;
		else{
			if(!tr[u][s[i]-'a']) tr[u][s[i]-'a']=++tot,fa[tot]=u;
			u=tr[u][s[i]-'a'];
		}
	}
	memcpy(Tr,tr,sizeof tr);
	Build();
	For(i,1,tot) e[fail[i]].push_back(i);
	Dfs(0);
	Read(m);
	For(i,1,m){
		int x,y;Read(x,y);
		qry[num[y]].push_back({x,i});
	}
	Dfs1(0);
	For(i,1,m){
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2021-10-26 17:21  Alan_Zhao_2007  阅读(35)  评论(0编辑  收藏  举报