P6257 [ICPC2019 WF] First of Her Name 题解
优越 AC 自动机永不败北。
思路
考虑给出的姓名的反串是一颗 Trie。
我们可以考虑将询问串也进行翻转。
这道题最有用的一点是什么呢。
可以发现 Trie 上任意一个到根的路径都是一个名字。
也就是只要我们的询问串在 Trie 中出现,出现的位置都可以对应到一个名字的后缀(翻转后)。
那么可以对询问串减出 AC 自动机。
然后把 Trie 放到 AC 自动机上跑即可。
时间复杂度:\(O(26(n+k))\)。
Code
/*
! 以渺小启程,以伟大结束。
! Created: 2024/06/30 16:00:54
*/
#include <bits/stdc++.h>
using namespace std;
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
const int N = 1e6 + 10;
int n, m, rt, ct;
int ch1[N][26], cr[N], dx[N];
int ch2[N][26], fa[N], sz[N], id[N];
vector<int> to[N];
inline void build() {
queue<int> q;
fro(i, 0, 25) if (ch2[0][i]) q.push(ch2[0][i]);
while (q.empty() == 0) {
int x = q.front(); q.pop();
fro(i, 0, 25)
if (ch2[x][i]) fa[ch2[x][i]] = ch2[fa[x]][i], q.push(ch2[x][i]);
else ch2[x][i] = ch2[fa[x]][i];
}
fro(i, 1, ct) to[fa[i]].push_back(i);
}
inline void solve() {
queue<int> q; q.push(0);
while (q.empty() == 0) {
int x = q.front();
q.pop(), sz[cr[x]] += dx[x];
fro(i, 0, 25) if (ch1[x][i])
cr[ch1[x][i]] = ch2[cr[x]][i], q.push(ch1[x][i]);
}
}
inline void dfs(int cr) {
for (auto i : to[cr]) {
dfs(i), sz[cr] += sz[i];
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
fro(i, 1, n) {
char x; int y;
cin >> x >> y;
if (!ch1[y][x - 'A']) ch1[y][x - 'A'] = i;
dx[ch1[y][x - 'A']]++;
}
fro(i, 1, m) {
string s;
cin >> s, rt = 0;
reverse(s.begin(), s.end());
for (auto j : s) {
if (!ch2[rt][j - 'A']) ch2[rt][j - 'A'] = ++ct;
rt = ch2[rt][j - 'A'];
}
id[i] = rt;
}
build();
solve(), dfs(0);
fro(i, 1, m) cout << sz[id[i]] << "\n";
return 0;
}