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;
}
Written by Alan_Zhao