Luogu P5840 【[COCI2015]Divljak】
Description
Solution
首先看到这题就想SAM对吧,然后qwaszx写了一发常数太大过不了就果断改AC自动机对吧。
考虑对\(S\)建立\(AC\)自动机,因为字符串的所有前缀的所有后缀是字符串的所有子串,而\(fail\)指针指的状态就是该状态的最长可识别后缀,所以在对\(S\)建好\(AC\)自动机之后就把插入的字符串扔到\(AC\)自动机上跑,它能到达的所有状态就是所有的前缀。这样再对每个状态跳所有的\(fail\)指针就能把该字符串的所有可识别子串在\(AC\)自动机上找出来了。
所以每次新插入字符串的时候只要在\(fail\)树上把所有经过的状态到\(root\)的链都加一,查询的时候查该字符串在\(AC\)自动机上的位置的值就行了。
直接这样做不大好做,所以考虑树上差分,先把所有的经过的状态都找出来,然后按\(dfs\)序排序,每次对每个状态单点加,对相邻状态的\(lca\)单点减。最后查询某个点的时候只需要查询子树和即可。
考虑为什么按照\(dfs\)序排序,原来的这些点是杂乱无章的,在按\(dfs\)序排序后就会按照从左往右的顺序依次修改,这样可以保证不重不漏。如果学过虚树的话应该很容易就能理解。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100000;
const int M = 2000000;
int head[M + 50], num, id[N + 50], tree[M + 50], trie[M + 50][26], cnt, root, top[M + 50], maxson[M + 50], siz[M + 50], dfx[M + 50], dfxx, f[M + 50], dep[M + 50], n, tmp[M + 50], len, fail[M + 50];
char st[M + 50];
struct Node
{
int next, to;
} edge[M + 50];
void Addedge(int u, int v)
{
edge[++num] = (Node){head[u], v};
head[u] = num;
return;
}
void Insert(char st[M + 50], int bh)
{
int l = strlen(st + 1), now = root;
for (int i = 1; i <= l; i++)
{
int c = st[i] - 'a';
if (!trie[now][c]) trie[now][c] = ++cnt;
now = trie[now][c];
}
id[bh] = now;
return;
}
void Build_AC_auto()
{
queue<int> q;
for (int i = 0; i <= 25; i++) if (trie[root][i]) q.push(trie[root][i]);
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = 0; i <= 25; i++)
if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]);
else trie[u][i] = trie[fail[u]][i];
}
return;
}
void Build_Fail_tree()
{
for (int i = 1; i <= cnt; i++) Addedge(fail[i], i);
return;
}
void Dfs1(int x)
{
dfx[x] = ++dfxx; siz[x] = 1;
int maxx = 0;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to; f[v] = x; dep[v] = dep[x] + 1;
Dfs1(v);
siz[x] += siz[v];
if (siz[v] > maxx) maxx = siz[v], maxson[x] = v;
}
return;
}
void Dfs2(int x, int topf)
{
top[x] = topf;
if (!maxson[x]) return;
Dfs2(maxson[x], topf);
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == maxson[x]) continue;
Dfs2(v, v);
}
return;
}
int Lca(int a, int b)
{
while (top[a] != top[b])
{
// cout << a << " " << b << endl;
if (dep[top[a]] < dep[top[b]]) swap(a, b);
a = f[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
int Lowbit(int x)
{
return x & (-x);
}
void Add(int pos, int val)
{
for (int i = pos; i <= cnt + 1; i += Lowbit(i)) tree[i] += val;
return;
}
int Query(int pos)
{
int ans = 0;
for (int i = pos; i; i -= Lowbit(i)) ans += tree[i];
return ans;
}
int Cmp(int a, int b)
{
return dfx[a] < dfx[b];
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%s", st + 1), Insert(st, i);
Build_AC_auto(); Build_Fail_tree(); dep[0] = 1; Dfs1(0); Dfs2(0, 0);
int q;
scanf("%d", &q);
int opt, ask;
while (q--)
{
scanf("%d", &opt);
if (opt == 1)
{
scanf("%s", st + 1); len = 0;
int now = root, l = strlen(st + 1);
for (int i = 1; i <= l; i++)
{
int c = st[i] - 'a';
now = trie[now][c];
tmp[++len] = now;
}
sort(tmp + 1, tmp + len + 1, Cmp);
len = unique(tmp + 1, tmp + len + 1) - tmp - 1;
for (int i = 1; i <= len; i++)
{
Add(dfx[tmp[i]], 1);
if (i > 1) Add(dfx[Lca(tmp[i], tmp[i - 1])], -1);
}
}
else
{
scanf("%d", &ask);
printf("%d\n", Query(dfx[id[ask]] + siz[id[ask]] - 1) - Query(dfx[id[ask]] - 1));
}
}
return 0;
}