結果
問題 |
No.430 文字列検索
|
ユーザー |
![]() |
提出日時 | 2019-11-02 12:48:13 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 21 ms / 2,000 ms |
コード長 | 3,452 bytes |
コンパイル時間 | 2,317 ms |
コンパイル使用メモリ | 208,672 KB |
最終ジャッジ日時 | 2025-01-08 02:17:50 |
ジャッジサーバーID (参考情報) |
judge5 / judge1 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 4 |
other | AC * 14 |
ソースコード
#include "bits/stdc++.h" using namespace std; using ll = long long; #define REP(i, n) for (int i = 0; i < n; ++i) #define ALL(v) v.begin(), v.end() using namespace std; // 入力文字列に対してマッチするパターンを検索 // O(N + M) // N := 入力文字列, M := パターン文字列の長さ合計 /* 1. パターンから Trie 木を作成. 2. 各ノードが表す文字列の末尾と一致するノードの内, 文字列長が最大のノードへ辺を張る. そのようなノードが存在しなければ根に辺を張る. この処理は bfs で可能. 各ノードはその祖先が一致する文字列の情報も持つ. 3. PMA(パターンマッチングオートマトン)の完成! */ // 出現文字 struct characters { // lower alphabets static int const size = 26; static int convert(char c) { return c - 'A'; } static char invert(int i) { return i + 'A'; } }; template <typename Char> struct AhoCorasick { static constexpr int invalid = -1; struct PMA { int fail; vector<int> next, accept; PMA() : fail(invalid), next(Char::size, invalid) {} }; const int K; vector<PMA> nodes; int next(int index, char cc) { int c = Char::convert(cc); int now = index; while (nodes[now].next[c] == invalid && now != 0) { now = nodes[now].fail; } now = nodes[now].next[c]; if (now == invalid) now = 0; return now; } AhoCorasick(const vector<string> &ts) : K((int)ts.size()) { const int root = 0; // root node nodes.emplace_back(); nodes[root].fail = root; for (int i = 0; i < K; i++) { int now = root; for (auto cs : ts[i]) { int c = Char::convert(cs); if (nodes[now].next[c] == invalid) { nodes[now].next[c] = (int)nodes.size(); nodes.emplace_back(); } now = nodes[now].next[c]; } nodes[now].accept.push_back(i); } queue<int> que; for (int c = 0; c < Char::size; c++) { if (nodes[root].next[c] != invalid) { nodes[nodes[root].next[c]].fail = root; que.push(nodes[root].next[c]); } } while (!que.empty()) { int now = que.front(); que.pop(); for (int c = 0; c < Char::size; c++) { if (nodes[now].next[c] != invalid) { que.push(nodes[now].next[c]); int nxt = next(nodes[now].fail, Char::invert(c)); nodes[nodes[now].next[c]].fail = nxt; for (auto ac : nodes[nxt].accept) { nodes[nodes[now].next[c]].accept.push_back(ac); } } } } // 最悪O(1)にする部分 for (int c = 0; c < Char::size; c++) { if (nodes[root].next[c] != invalid) { que.push(nodes[root].next[c]); } else { nodes[root].next[c] = root; } } while (!que.empty()) { int p = que.front(); que.pop(); for (int c = 0; c < Char::size; c++) { if (nodes[p].next[c] == invalid) { int nxt = nodes[nodes[p].fail].next[c]; assert(nxt != invalid); nodes[p].next[c] = nxt; } else { que.push(nodes[p].next[c]); } } } // ここまで } vector<vector<int>> match(const string &str) { vector<vector<int>> ret(K); int now = 0; for (int i = 0; i < (int)str.size(); i++) { now = next(now, str[i]); for (auto k : nodes[now].accept) { ret[k].push_back(i); } } return ret; } }; int main() { string s; cin >> s; int m; cin >> m; vector<string> v(m); REP(i, m) cin >> v[i]; AhoCorasick<characters> aho(v); ll ans = 0; auto res = aho.match(s); for (auto a : res) ans += a.size(); cout << ans << endl; }