输入一颗字典树,之后m个查询,每个查询给出一个串,问这个串在字典树某一个节点代表的串中出现了多少次。
一个串在另一个串中出现了多少次是sam的基操,但是这里输入了一颗字典树,4e5个节点,于是后缀自动机维护的难度和复杂度就变得巨大,分析了一下,似乎是不可做的。
久违的用了一次ac自动机来解决一个串在另一个串里出现了多少次的问题,只需要建出fail树,这个问题就变成了一个串的链有多少个节点在另一个串的子树里。使用dfs序+树状数组or线段树即可解决。
一开始把查询串也建入了字典树,超时。没办法只能单独把查询串建ac自动机,然后把输入的字典树放到ac自动机上跑(相当于一个取出字典树所代表的串),边跑边把经过的节点+1,然后遇到了需要查询的节点,就查询区间和,最后回溯的时候再把经过的节点-1。
ac代码:
#include
using namespace std;
const int maxn = 4e5 + 5;
char s[maxn];
struct AC_Automaton {
int next[maxn][26];
vector<pair<int, int>> q[maxn], trie[maxn];
int ans[maxn];
int fail[maxn];
vector<int> tree[maxn];
int tot, in[maxn], out[maxn];
int sz, root;
int newNode() {
return sz++;
}
void init() {
sz = 0;
root = newNode();
}
int add() {
int p = root;
for (int i = 0, c; s[i]; ++i) {
c = s[i] - 'a';
if (!next[p][c]) {
next[p][c] = newNode();
}
p = next[p][c];
}
return p;
}
void getFail() {
queue<int> q;
for (int i = 0; i < 26; i++) {
if (next[root][i]) {
q.push(next[root][i]);
} else {
next[root][i] = root;
}
}
while (!q.empty()) {
int p = q.front();
q.pop();
tree[fail[p]].push_back(p);
for (int i = 0; i < 26; i++) {
if (next[p][i]) {
fail[next[p][i]] = next[fail[p]][i];
q.push(next[p][i]);
} else {
next[p][i] = next[fail[p]][i];
}
}
}
}
int t[maxn << 2];
void update(int rt, int l, int r, int pos, int val) {
if (l == r) {
t[rt] += val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) {
update(rt << 1, l, mid, pos, val);
} else {
update(rt << 1 | 1, mid + 1, r, pos, val);
}
t[rt] = t[rt << 1] + t[rt << 1 | 1];
}
int query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return t[rt];
}
int mid = (l + r) >> 1, ans = 0;
if (L <= mid) {
ans += query(rt << 1, l, mid, L, R);
}
if (R > mid) {
ans += query(rt << 1 | 1, mid + 1, r, L, R);
}
return ans;
}
void dfs(int x) {
in[x] = ++tot;
for (auto v : tree[x]) {
dfs(v);
}
out[x] = tot;
}
void dfs2(int x, int p) {
update(1, 1, tot, in[p], 1);
for (auto e:q[x]) {
ans[e.second] = query(1, 1, tot, in[e.first], out[e.first]);
}
for (auto v:trie[x]) {
dfs2(v.first, next[p][v.second]);
}
update(1, 1, tot, in[p], -1);
}
void solve() {
init();
int n, m, op, fa;
char c;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &op);
if (op == 1) {
scanf(" %c", &c);
trie[0].push_back({i, c - 'a'});
} else {
scanf("%d %c", &fa, &c);
trie[fa].push_back({i, c - 'a'});
}
}
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
scanf("%d %s", &fa, s);
q[fa].push_back({add(), i});
}
getFail();
dfs(root);
dfs2(0, root);
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
}
} ac;
int main() {
ac.solve();
return 0;
}