题目大意:
先以特定模式按顺序给出了N个单词,给出M个二元组(x,y)询问:给出的第x个单词在第y个单词中出现了几次。(N,M<=10^5)
解题思路:
1、建立ac自动机的过程就不在赘述。
2、首先需要了解或者发现fail树,fail树是指AC自动机上由fail链构成的树。由于每个状态点入度为一,且根据BFS的构造两点的深度满足递增关系,因此不会出现环,从而是一棵树。
3、fail树具有如下性质:在某一点沿着fail树走向根,将遍历所有以该点至Trie根这一字符串的后缀为前缀的单词所对应的前缀最后一个字符节点位置。
4、因此如果将y单词的所有节点标记为1,那么x在y中出现的次数就是fail树中以x终止节点为根的子树中1的个数。
5、子树统计问题常用DFS序列转化为区间统计问题。大规模询问可以通过离线重新组织询问次序使得更方便快捷求解。于是我们将对y做邻接表,链出所有询问x,离线处理。
6、这样我们沿着AC自动机的Trie树DFS,每到一个节点将他的DFS序在树状数组中加1,如果是y对应的节点,处理所有询问x就是统计对应x的fail子树1的个数,递归结束返回前再将他的DFS序在树状数组中减1。问题就得到解决。
/**************************************************************
Problem: 2434
User: xushu
Language: C++
Result: Accepted
Time:600 ms
Memory:30404 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxs=1e5+100;
char s[maxs];
int ans[maxs];
struct tree_edge{
int e[maxs],nex[maxs],cnt,head[maxs];
void init(int n){ cnt=0;for (int i=0;i<=n;i++) head[i]=-1;}
void add_edge(int x,int y){
e[cnt]=y;nex[cnt]=head[x];head[x]=cnt++;
}
} ted;
struct question{
struct node{
int y,num;
} ;
node e[maxs];
int nex[maxs],cnt,head[maxs];
void init(int n){ cnt=0;for (int i=0;i<=n;i++) head[i]=-1;}
void add_edge(int x,int y,int i){
e[cnt]=(node){y,i};nex[cnt]=head[x];head[x]=cnt++;
}
} que;
struct acauto{
int cnt,ch[maxs][26],fa[maxs],f[maxs],val[maxs],id,adr[maxs],och[maxs][26];
int last[maxs];
int l;
int idx(char x) {return x-'a';}
void init(){
cnt=0;
memset(ch[0],0,sizeof(ch[0]));
memset(och[0],0,sizeof(och[0]));
f[0]=id=0;
}
int newnode(){
cnt++;
memset(och[cnt],0,sizeof(och[cnt]));
memset(ch[cnt],0,sizeof(ch[cnt]));
return cnt;
}
void solve(){
int i,v,u;
l=strlen(s);
for (i=0;i<l;i++) val[i]=0;
u=0;i=0;
while (i<l){
if (s[i]=='P') {
if (val[u]) {
adr[++id]=u;
} else {
val[u]=++id;
adr[id]=u;
}
i++;continue;
}
if (s[i]=='B') {
if (u) u=fa[u];
i++;
continue;
}
v=idx(s[i]);
if (ch[u][v]) u=ch[u][v],i++;
else {
och[u][v]=ch[u][v]=newnode();
fa[ch[u][v]]=u;
u=ch[u][v];i++;
}
}
}
void get_fail(){
queue<int> q;
int i,u;
for (i=0;i<26;i++) if (ch[0][i]) {f[ch[0][i]]=0;q.push(ch[0][i]);ted.add_edge(0,ch[0][i]);last[ch[0][i]]=0;}
while (!q.empty()){
int r=q.front();q.pop();
for (i=0;i<26;i++){
u=ch[r][i];
if (!u){ ch[r][i]=ch[f[r]][i];continue;}
f[u]=ch[f[r]][i];
ted.add_edge(ch[f[r]][i],u);
last[u]=val[f[u]] ? f[u]:last[f[u]];
q.push(u);
}
}
}
};
struct Fenwick{
int c[2*maxs],n;
void init(){for (int i=0;i<=n;i++) c[i]=0;}
int lowbit(int x) {return x&(-x);}
void add(int i,int x){
while (i<=n){
c[i]+=x;
i+=lowbit(i);
}
}
int sum(int i){
int ans=0;
while (i>0){
ans+=c[i];
i-=lowbit(i);
}
return ans;
}
} ;
Fenwick fk;
acauto ac;
int did;
int l[maxs],r[maxs];
void dfs_id(int u){
l[u]=++did;
for (int i=ted.head[u];i!=-1;i=ted.nex[i]){
dfs_id(ted.e[i]);
}
r[u]=++did;
}
void dfs(int u){
int i,v,now;
fk.add(l[u],1);
for (i=que.head[u];i!=-1;i=que.nex[i]){
v=que.e[i].num;
now=que.e[i].y;
ans[v]=fk.sum(r[now])-fk.sum(l[now]-1);
}
for (i=0;i<26;i++){
if (!ac.och[u][i]) continue;
dfs(ac.och[u][i]);
}
fk.add(l[u],-1);
}
int main()
{
int m;
int x,y;
scanf("%s",s);
int ll=strlen(s);
ted.init(ll);
ac.init();
ac.solve();
ac.get_fail();
did=0;
dfs_id(0);
scanf("%d",&m);
que.init(ll);
for (int k=1;k<=m;k++){
scanf("%d %d",&x,&y);
x=ac.adr[x];y=ac.adr[y];
que.add_edge(y,x,k);
}
fk.n=did;
fk.init();
dfs(0);
for (int k=1;k<=m;k++) printf("%d\n",ans[k]);
return 0;
}