题目链接。
题解
虽然总长度我们不能确定,但是如果跟着第一行字符串跑建 AC 自动机,节点数肯定 (le 10^5) 的,因为每个字符最多增加一个节点。
考虑一个查询 ((x, y)), 即把字符串 (s[y]) 的每个点对应在图上的点 (+1),然后询问 (x) 对应节点的子树和。
子树和想到 (dfs) 序差分 (+) 树状数组
离线,把询问 (x) 打进 (d[y]) 里。 考虑对于每个 (y),先修改,然后询问对应的 (x)。这里的每个字符串既是匹配串也是模式串,所以 (s[y]) 对应的节点就是一条从根出发的链。第二次再跟着输入的第一行字符串跑 (AC) 自动机,动态维护将根节点到当前节点上的所有点 (+1),遇到一个 P 就停下来查询即可。
注意 “B” 退回时,要删除对应的影响。
时间复杂度
(O((N + M)logN))
小细节
- 由于要按 B 回退,可以记录一个 (fa) 数组模拟往回跳。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 100005;
typedef long long LL;
char s[N];
int n, m, cnt, dfncnt, fail[N], match[N], fa[N], c[N], tr[N][26];
int id[N], ans[N], dfn[N], sz[N], q[N], idx;
// BIT
void add(int x, int k) {
for (; x <= dfncnt; x += x & -x) c[x] += k;
}
int ask(int x) {
int res = 0;
for (; x; x -= x & -x) res += c[x];
return res;
}
// 询问 x 的子树和
int query(int x) {
return ask(dfn[x] + sz[x] - 1) - ask(dfn[x] - 1);
}
// 询问
struct Q{
// 求 x 的子树和,给 ans[id] 的贡献是 +V,
int x, id;
};
vector<Q> d[N]; // q[i] 表示插入恰好为 i 个字符串需要的询问
// fail 树的边
int head[N], numE = 0;
struct E{
int next, v;
} e[N];
void addEdge(int u, int v) {
e[++numE] = (E) { head[u], v };
head[u] = numE;
}
// AC 自动机:插入
void insert() {
int p = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == 'B') p = fa[p];
else if (s[i] == 'P') id[i] = ++cnt, match[cnt] = p;
else {
int ch = s[i] - 'a';
if (!tr[p][ch]) tr[p][ch] = ++idx, fa[idx] = p;
p = tr[p][ch];
}
}
}
void dfs(int u) {
dfn[u] = ++dfncnt, sz[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
dfs(v);
sz[u] += sz[v];
}
}
// 建 fail
void build() {
int hh = 0, tt = -1;
for (int i = 0; i < 26; i++)
if (tr[0][i]) q[++tt] = tr[0][i];
while (hh <= tt) {
int u = q[hh++];
for (int i = 0; i < 26; i++) {
int v = tr[u][i];
if (v) {
fail[v] = tr[fail[u]][i];
q[++tt] = v;
} else tr[u][i] = tr[fail[u]][i];
}
}
for (int i = 1; i <= idx; i++) addEdge(fail[i], i);
dfs(0);
}
void work() {
int p = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == 'B') {
add(dfn[p], -1);
p = fa[p];
} else if (s[i] == 'P') {
int x = id[i];
for (int j = 0; j < d[x].size(); j++) {
Q k = d[x][j];
ans[k.id] += query(match[k.x]);
}
} else {
p = tr[p][s[i] - 'a'];
add(dfn[p], 1);
}
}
}
int main() {
scanf("%s%d", s + 1, &m);
n = strlen(s + 1);
for (int i = 1, x, y; i <= m; i++) {
scanf("%d%d", &x, &y);
d[y].push_back((Q){ x, i });
}
insert();
build();
work();
for (int i = 1; i <= m; i++) printf("%d
", ans[i]);
return 0;
}