Loading

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;
}
posted @ 2024-06-30 16:24  JiaY19  阅读(2)  评论(0编辑  收藏  举报