Solution [NOI2011]阿狸的打字机
题目大意:给定一颗(Trie)树,多次询问(x)号节点代表的字符串在(y)号节点代表的字符串中出现了多少次
AC自动机
分析:首先我们回顾一下(AC)自动机上匹配的过程
我们沿着文本串在(Trie)图上面走(假设已经用类似路径压缩的办法,把(Trie)树给补全,当然此题直接从根走到(y)即可),走到每个节点,一直沿着(fail)跳,跳到的节点代表能匹配(当前节点代表前缀的后缀)的前缀(有点绕加了个括号)。
跳到的节点如果是某个字符串的结尾,那么就匹配上了。
那么这题的暴力算法就是从根走到(y),路径上每个节点跳(fail),走到(x)结尾就把答案加(1)
显然复杂度无法承受,我们考虑从(x)的角度来考虑
将(fail)边反向就可以得到(fail)树,实际上我们只需要查询(x)为根的子树内,有多少个节点在根到(y)的链
可以在线做,也可以离线,利用(dfs)序来统计
我们将询问((x,y))保存在(y)内,进行一次(dfs),在新进入一个节点时在它(dfs)序位置(+1),离开时撤销。对于它所有的询问,直接查询子树和即可
树状数组可以维护
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 1e5 + 100;
struct mpair{int first,second;};
inline mpair make_pair(int x,int y){return mpair{x,y};}
extern int ans[maxn];
namespace bit{
int f[maxn];
inline int lowbit(int x){return x & (-x);}
inline void add(int pos,int x){
while(pos < maxn){
f[pos] += x;
pos += lowbit(pos);
}
}
inline int query(int pos){
int res = 0;
while(pos){
res += f[pos];
pos -= lowbit(pos);
}
return res;
}
inline int query(int a,int b){return query(b) - query(a - 1);}
}
namespace graph{
vector<int> G[maxn];
inline void addedge(int from,int to){G[from].push_back(to);}
int siz[maxn],dfn[maxn],dfs_tot;
inline void dfs(int u){
dfn[u] = ++dfs_tot;
siz[u] = 1;
for(int v : G[u])dfs(v),siz[u] += siz[v];
}
}
namespace ac{
const int siz = 26;
int ch[maxn][siz],chhis[maxn][siz],faz[maxn],fail[maxn],tot;
vector<mpair> query[maxn];
inline int idx(char c){return c - 'a';}
inline void build(){
memcpy(chhis,ch,sizeof(ch));
queue<int> Q;
for(int c = 0;c < siz;c++)
if(ch[0][c])Q.push(ch[0][c]);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int c = 0;c < siz;c++)
if(ch[u][c])fail[ch[u][c]] = ch[fail[u]][c],Q.push(ch[u][c]);
else ch[u][c] = ch[fail[u]][c];
}
for(int u = 1;u <= tot;u++)graph::addedge(fail[u],u);
}
inline void dfs(int u){
// printf("%d
",u);
if(u)bit::add(graph::dfn[u],1);
if(u)for(auto p : query[u]){
// printf("")
ans[p.first] = bit::query(graph::dfn[p.second],graph::dfn[p.second] + graph::siz[p.second] - 1);
}
for(int c = 0;c < siz;c++)
if(chhis[u][c])dfs(chhis[u][c]);
if(u)bit::add(graph::dfn[u],-1);
}
}
char str[maxn];
int m,now,pos[maxn],ans[maxn],print;
int main(){
#ifdef LOCAL
freopen("type3.in","r",stdin);
#endif
scanf("%s",str + 1);
for(int i = 1;str[i];i++){
if(str[i] == 'B')now = ac::faz[now];
else if(str[i] == 'P')pos[++print] = now;
else{
int c = ac::idx(str[i]);
if(!ac::ch[now][c])ac::ch[now][c] = ++ac::tot,ac::faz[ac::tot] = now;
now = ac::ch[now][c];
}
}
ac::build();
graph::dfs(0);
scanf("%d",&m);
for(int x,y,i = 1;i <= m;i++){
scanf("%d %d",&x,&y);
ac::query[pos[y]].push_back(make_pair(i,pos[x]));
}
ac::dfs(0);
for(int i = 1;i <= m;i++)printf("%d
",ans[i]);
return 0;
}