結果
| 問題 |
No.430 文字列検索
|
| コンテスト | |
| ユーザー |
eto_nagisa
|
| 提出日時 | 2019-11-02 12:50:20 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 25 ms / 2,000 ms |
| コード長 | 3,524 bytes |
| コンパイル時間 | 2,537 ms |
| コンパイル使用メモリ | 211,712 KB |
| 最終ジャッジ日時 | 2025-01-08 02:18:01 |
|
ジャッジサーバーID (参考情報) |
judge3 / 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<unique_ptr<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.push_back(make_unique<PMA>());
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.push_back(make_unique<PMA>());
}
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;
}
eto_nagisa