题解
当然有广义 SAM 做法,但我用的是 AC 自动机。
设 (n) 个输出的串为 (langle s_1,s_2,dots,s_n angle)。
题目中给出的字符串实际上就是一棵 Trie 树,对这棵 Trie 建立 ACAM。
考虑“子串”实际上就是前缀的后缀。对于任意的串 (S),若串 (S) 在 Trie 上的对应节点为 (u),那么 (S) 的所有前缀就对应着 (u) 的所有祖先。而在 fail 树上的一个点,它的所有出现过的后缀就是它在 fail 树上的所有祖先。
对于一个询问 ((x,y)),我们要统计 (s_x) 在 (s_y) 中的出现次数。设 (s_x) 的对应节点为 (u),(s_y) 对应 (v),那么也就相当于求 (sum_{xin operatorname{subtree}(u)} [xin 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
",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
",ans[i]);
}
return 0;
}