結果
| 問題 |
No.430 文字列検索
|
| コンテスト | |
| ユーザー |
ほしのん(hoshinon)
|
| 提出日時 | 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;
}
ほしのん(hoshinon)