[COCI2015]Divljak

\(\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));
	}
}
posted @ 2022-07-10 22:13  leiyuanze  阅读(31)  评论(0编辑  收藏  举报