結果
問題 |
No.430 文字列検索
|
ユーザー |
![]() |
提出日時 | 2025-07-27 21:40:53 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 14 ms / 2,000 ms |
コード長 | 4,200 bytes |
コンパイル時間 | 945 ms |
コンパイル使用メモリ | 104,988 KB |
実行使用メモリ | 7,716 KB |
最終ジャッジ日時 | 2025-07-27 21:40:56 |
合計ジャッジ時間 | 2,464 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 4 |
other | AC * 14 |
ソースコード
#include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <iomanip> using namespace std; typedef long long ll; // const ll INF64 = 1LL << 60; const ll INF64 = ((1LL<<62)-(1LL<<31)); // 10^18より大きく、かつ2倍しても負にならない数 const int INF32 = 0x3FFFFFFF; // =(2^30)-1 10^9より大きく、かつ2倍しても負にならない数 template<class T> inline bool chmin(T &a, T b) { if(a > b) { a = b; return true; } return false; } template<class T> inline bool chmax(T &a, T b) { if(a < b) { a = b; return true; } return false; } #define YesNo(T) cout << ((T) ? "Yes" : "No") << endl; // T:bool // https://yukicoder.me/problems/no/430 ll ans = 0; // https://algo-logic.info/trie-tree/ // Trie<26, 'A'> trie; のように定義する // (1)insert(word) : 文字列wordをトライ木に追加する // (2)search(word, prefix=true/false) : 文字列がトライ木に追加されているか検索 // (3)word_count() : 追加した単語数を返す // (4)size() : 頂点の総数 template <int char_size, int base> // 文字の種類と、0に対応する文字 struct Trie { private: struct Node { vector<int> next; // next[ch]:文字chである子の頂点番号 存在しなければ-1 vector<int> accept; // 末端がこの頂点である文字列のidたち int c; // この頂点が持つ文字を、base基点で数値で表したもの 'A'なら0, 'B'なら1, など int depth; // 根からの距離 int common; // この頂点を共有している文字列の個数 Node(int c_, int dep_) : c(c_), depth(dep_), common(0) { next.assign(char_size, -1); } }; vector<Node> nodes; // トライ木本体 int root; // 根 (コンストラクタでしか使わないが、明示的に) // 単語wordをid番目として追加 void insert(const string &word, int id) { int node_id = 0; // node_id=0は根(文字が入っていない)であることに注意 for(int i = 0; i < (int)word.size(); i++) { // 以下、node_idから文字cへたどった先がnext_idになる int c = (int)(word[i] - base); int &next_id = nodes[node_id].next[c]; if(next_id == -1) { // 文字cが木に存在しなければ追加 next_id = (int)nodes.size(); nodes.push_back(Node(c, nodes[node_id].depth+1)); } nodes[node_id].common++; node_id = next_id; } // word末尾の文字は、このタイミングでのnode_idである点に注意 nodes[node_id].common++; nodes[node_id].accept.push_back(id); } public: Trie() : root(0) { nodes.push_back(Node(root, 0)); // 始めは根のみ } // wordをトライ木に追加 void insert(const string &word) { insert(word, nodes[0].common); } // wordの探索 // prefix=false : wordがTrie木に存在すればtrue // prefix=true : wordそのものでなくとも、wordを接頭辞(prefix)として持つ単語が存在すればtrue bool search(const string &word, bool prefix = false) { int node_id = 0; for(int i = 0; i < (int)word.size(); i++) { // 本問題用にmod if(nodes[node_id].accept.size() > 0) ans++; int c = (int)(word[i] - base); int &next_id = nodes[node_id].next[c]; if(next_id == -1) { return false; // 次の頂点が存在しない } node_id = next_id; } // word末尾の文字は、このタイミングでのnode_idである点に注意 // 本問題用にmod if(nodes[node_id].accept.size() > 0) ans++; if(prefix) { return true; // 接頭辞であれば、ここまで来た時点で確実に存在する } else { return nodes[node_id].accept.size() > 0; // 検索終端が末端となるような頂点があるか } } // 追加した単語数 int word_count() const { return nodes[0].common; } // 頂点の総数 (根を含むので文字数+1になる) int size(void) const { return (int)nodes.size(); } }; int main(void) { ll i; string s; cin >> s; ll M; cin >> M; Trie<26, 'A'> tr; for(i = 0; i < M; i++) { string c; cin >> c; tr.insert(c); } ll slen = (ll)s.size(); for(i = 0; i < slen; i++) // i文字目から10文字取る { string ss = s.substr(i, 10); tr.search(ss); } cout << ans << endl; return 0; }