\(\text{Analysis}\)
\(ACAM\) 毕业题:\(\text{[COCI2015]Divljak}\)
确信
\(AC\) 自动机加树链剖分加树状数组,加深了对 \(fail\) 数组的理解
由 \(AC\) 自动机的工作模式知先用模式串建出 \(Trie\) 和 \(fail\) 数组
然后加入字符串操作,考虑这个字符串能包含那些模式串,给这些模式串答案加 \(1\)
模拟文本串在自动机上匹配的过程,有一个不断跳 \(fail\) 指针的过程
对于 \(fail\) 位置上存在的模式串,答案均要加 \(1\)
在多个询问中,这是一个极其缓慢的过程
考虑 \(Trie\) 上所有 \(fail_x\) 到 \(x\) 的边形成一个 \(fail\) 树
不断跳 \(fail\) 指针的过程即一个点跳到根的过程
这样就可以用数据结构处理了
考虑一个串的加入经过 \(AC\) 自动机上若干个关键点
这些关键点在 \(fail\) 树中到根的点集的并就是我们要维护的点
要维护的点答案加且只加 \(1\)
不难想到只将关键点加 \(1\),询问时找到当前字符串在 \(fail\) 树上的点,将以它为根的子树记录的贡献相加即可
发现一个插入操作中多个关键点对它们的 \(LCA\) 及以上的点只贡献 \(1\)
将关键点按 \(dfs\) 序排序后,相邻点的 \(LCA\) 减 \(1\) 即可
修改和查询均可用树状数组完成
\(\text{Code}\)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define RE register
#define IN inline
using namespace std;
const int N = 2e6 + 5;
int n, id[N], size = 1, h[N], tot, Q[N];
char s[N];
struct edge{int to, nxt;}e[N];
IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
struct ACAM{
int tr[N][26], fail[N];
IN void insert(int x)
{
int len = strlen(s), u = 1, c;
for(RE int i = 0; i < len; i++)
{
c = s[i] - 'a';
if (!tr[u][c]) tr[u][c] = ++size;
u = tr[u][c];
}
id[x] = u;
}
IN void getfail()
{
int h = 0, t = 0, now;
for(RE int i = 0; i < 26; i++)
if (tr[1][i]) Q[++t] = tr[1][i], fail[Q[t]] = 1;
else tr[1][i] = 1;
while (h < t)
{
now = Q[++h];
for(RE int i = 0; i < 26; i++)
if (tr[now][i]) fail[tr[now][i]] = tr[fail[now]][i], Q[++t] = tr[now][i];
else tr[now][i] = tr[fail[now]][i];
}
for(RE int i = 2; i <= size; i++) add(fail[i], i);
}
}AC;
int top[N], siz[N], dfn[N], dfc, son[N], rev[N], fa[N], dep[N];
void dfs1(int x)
{
siz[x] = 1, dep[x] = dep[fa[x]] + 1;
for(RE int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
fa[v] = x, dfs1(v), siz[x] += siz[v];
if (siz[son[x]] < siz[v]) son[x] = v;
}
}
void dfs2(int x, int t)
{
top[x] = t, dfn[x] = ++dfc, rev[dfc] = x;
if (son[x]) dfs2(son[x], t);
for(RE int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == son[x]) continue;
dfs2(v, v);
}
}
IN int LCA(int x, int y)
{
int fx = top[x], fy = top[y];
while (fx ^ fy)
{
if (dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
x = fa[fx], fx = top[x];
}
if (dep[x] < dep[y]) return x;
return y;
}
struct BIT{
int c[N];
IN int lowbit(int x){return x & (-x);}
IN void add(int x, int v){if (x > 1) for(; x <= size; x += lowbit(x)) c[x] += v;}
IN int Query(int x){int s = 0; for(; x; x -= lowbit(x)) s += c[x]; return s;}
}T;
IN bool cmp(int x, int y){return dfn[x] < dfn[y];}
IN void update()
{
int len = strlen(s), u = 1, c, t = 0;
for(RE int i = 0; i < len; i++)
c = s[i] - 'a', u = AC.tr[u][c], Q[++t] = u, T.add(dfn[u], 1);
sort(Q + 1, Q + t + 1, cmp);
for(RE int i = 1; i < t; i++) T.add(dfn[LCA(Q[i], Q[i + 1])], -1);
}
IN void read(int &x)
{
x = 0; char ch = getchar(); int f = 1;
for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
x *= f;
}
int main()
{
read(n);
for(RE int i = 1; i <= n; i++) scanf("%s", s), AC.insert(i);
AC.getfail(), dfs1(1), dfs2(1, 1); int q; read(q);
for(int op, x; q; --q)
{
read(op);
if (op == 1) scanf("%s", s), update();
else read(x), printf("%d\n", T.Query(dfn[id[x]] + siz[id[x]] - 1) - T.Query(dfn[id[x]] - 1));
}
}