Bzoj 2434
题意:见上。
先介绍 Trie,Trie图,Fail树
将字符串插入 Trie 即可得到下图
将失配函数连上,得到Trie图
Trie图可以将字符串问题转化为图论问题,具体可以看Bzoj 2938
将原边删掉,失配边反向,得到Fail树
Fail树每个点的祖先节点都是这个点失配后可以走到的点。
对于AC自动机中的每一个节点,如果节点A的失配边指向节点B,就会发现B对应的字符串一定在A对应的字符串中出现,那么在Fail树上显然可以利用这一性质。
考虑暴力,即在Fail树上往上跳。
我们还可以离线,将$y$相等的并成一块,一起处理,能过$70$分。
逆向思维,我们考虑对于每个$x$去找$y$,那么标记一下$y$能跳到的点,求子树和即可。显然可以树状数组维护DFS序。
而这样还是不能通过。我们发现每次初始化$y$能跳到的点进树状数组时,时间开销非常大。我们考虑边加边统计。
我们在原Trie树上DFS,将当前点$u$根路径上的点标记为$1$,然后再Fail树上求每个$u$对应的询问的$x$的子树和即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const int MAXN = 100000 + 5;
// AC自动机
int Q, ch[MAXN][26], fa[MAXN], f[MAXN], val[MAXN], sz, pos[MAXN];
char s[MAXN];
queue<int > q;
void getFail() {
for (int c = 0; c < 26; ++c) {
int v = ch[0][c];
if (v) q.push(v), f[v] = 0;
}
while (!q.empty()) {
int u = q.front(); q.pop();
for (int c = 0; c < 26; ++c) {
int v = ch[u][c];
if (!v) continue ;
q.push(v);
int j = f[u];
while (j && !ch[j][c]) j = f[j];
f[v] = ch[j][c];
}
}
}
// Fail树
vector<int > G[MAXN];
int dfn[MAXN], rr[MAXN], idx;
void ins(int u, int v) {G[u].push_back(v);}
void dfs_pre(int u, int p) {
dfn[u] = ++idx;
for (int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i];
if (v != p) dfs_pre(v, u);
}
rr[u] = idx;
}
// 树状数组
int c[MAXN];
int lowbit(int x) {return x & (-x);}
void add(int x, int v) {for (int i = x; i <= sz + 1; i += lowbit(i)) c[i] += v;}
int query(int x) {int ret = 0; for (int i = x; i > 0; i -= lowbit(i)) ret += c[i]; return ret;}
// 询问
struct qry {
int x, y, id;
bool operator < (const qry &rhs) const {return y < rhs.y;}
} xw[MAXN];
int ql[MAXN], qr[MAXN], ans[MAXN];
void dfs_ans(int u, int p) {
add(dfn[u], 1);
if (val[u]) for (int i = ql[u]; i <= qr[u]; ++i)
ans[xw[i].id] = query(rr[pos[xw[i].x]]) - query(dfn[pos[xw[i].x]] - 1);
for (int c = 0; c < 26; ++c) {
int v = ch[u][c];
if (v) dfs_ans(v, u);
}
add(dfn[u], -1);
}
void clean() {
ms(ch, 0), ms(fa, 0), ms(f, 0), ms(val, 0), sz = 0;
ms(dfn, 0), ms(rr, 0), idx = 0;
ms(c, 0);
}
int solve() {
clean();
scanf("%s", s);
int len = strlen(s), now = 0, cnt = 0;
for (int i = 0; i < len; ++i) {
if (s[i] == 'P')
val[now] = 1, pos[++cnt] = now;
else if (s[i] == 'B')
now = fa[now];
else {
int c = s[i] - 'a';
if (!ch[now][c]) ch[now][c] = ++sz;
fa[ch[now][c]] = now, now = ch[now][c];
}
}
getFail();
for (int i = 1; i <= sz; ++i) ins(i, f[i]), ins(f[i], i);
dfs_pre(0, -1);
cin >> Q;
for (int i = 1; i <= Q; ++i) scanf("%d%d", &xw[i].x, &xw[i].y), xw[i].id = i;
sort(xw + 1, xw + 1 + Q);
for (int p = 1, i = 1; i <= Q; i = p) {
ql[pos[xw[i].y]] = qr[pos[xw[i].y]] = i;
while (xw[i].y == xw[++p].y) ++qr[pos[xw[i].y]];
}
dfs_ans(0, -1);
for (int i = 1; i <= Q; ++i) printf("%d\n", ans[i]);
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}
/*
abbPcP
1
1 2
abbPcP
1
1 1
*/