結果
| 問題 |
No.430 文字列検索
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-10-11 22:10:32 |
| 言語 | Rust (1.83.0 + proconio) |
| 結果 |
AC
|
| 実行時間 | 19 ms / 2,000 ms |
| コード長 | 4,101 bytes |
| コンパイル時間 | 12,100 ms |
| コンパイル使用メモリ | 398,232 KB |
| 実行使用メモリ | 7,716 KB |
| 最終ジャッジ日時 | 2025-10-11 22:10:47 |
| 合計ジャッジ時間 | 13,609 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 14 |
コンパイルメッセージ
warning: unused import: `BTreeSet`
--> src/main.rs:9:40
|
9 | use std::collections::{HashSet,HashMap,BTreeSet};
| ^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: type `node` should have an upper camel case name
--> src/main.rs:14:8
|
14 | struct node {
| ^^^^ help: convert the identifier to upper camel case: `Node`
|
= note: `#[warn(non_camel_case_types)]` on by default
warning: value assigned to `parent_hash` is never read
--> src/main.rs:45:13
|
45 | let mut parent_hash:usize = 0;
| ^^^^^^^^^^^
|
= help: maybe it is overwritten before being read?
= note: `#[warn(unused_assignments)]` on by default
warning: unused variable: `i`
--> src/main.rs:79:13
|
79 | for i in (0..node.length).rev() {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
|
= note: `#[warn(unused_variables)]` on by default
ソースコード
/*
* Author: srtry
* Created: 2025-10-11T04:41:48+09:00
* Coding: utf-8-unix
*/
use proconio::input;
use proconio::marker::{Chars};
use std::collections::{HashSet,HashMap,BTreeSet};
use std::io::{stdout,Write,BufWriter};
use std::u8;
#[derive(PartialEq,Eq,Hash,Debug,Clone)]
struct node {
length:usize,
children:[bool;26],
direct_cnt:usize,
failure:usize,
}
impl node {
fn new() -> Self {
node {length: 0, children: [false;26], direct_cnt: 0, failure:0}
}
}
fn hash(x:&[char]) -> usize {
let mut tmp: usize = 0;
for ch in x.iter().rev() {
tmp = tmp*27 + 1 + (*ch as u8 - b'A') as usize
}
return tmp
}
fn main() {
input!{
s:Chars,
m:usize,
c:[Chars;m]
}
let out = stdout();
let mut out = BufWriter::new(out.lock());
// trieの構築
let mut trie:HashMap<usize,node> = HashMap::new();
trie.insert(0,node::new());
let mut parent_hash:usize = 0;
for c_elem in c.iter() {
parent_hash = 0;
for i in 1..=c_elem.len() {
let hash = hash(&c_elem[0..i]);
let idx: usize = c_elem[i-1] as usize - b'A' as usize;
let mut node = node{
length:i,
children:[false;26],
direct_cnt:0,
failure:0
};
if i==c_elem.len() {
node.direct_cnt = 1;
}
// 現在ノードのハッシュが存在するか
// 存在 => direct_cntの更新、parent_hashを今のハッシュに
// 非存在 => trieに追加、parent_hashを今のハッシュに
if let Some(x) = trie.get_mut(&hash) {
x.direct_cnt = node.direct_cnt.max(x.direct_cnt);
} else {
trie.insert(hash, node);
}
// 親ノードのchildrenを更新
trie.get_mut(&parent_hash).unwrap().children[idx] = true;
// 今回のノードを親ノードへ
parent_hash = hash;
}
}
// failureの構築
let hashes:HashSet<usize> = trie.keys().cloned().collect();
for (hash,node) in trie.iter_mut() {
let mut failure_hash = hash.clone();
for i in (0..node.length).rev() {
failure_hash /= 27;
if hashes.contains(&failure_hash) {
node.failure = failure_hash;
break;
}
}
}
// direct_cntの更新
let trie_ = trie.clone();
for node in trie.values_mut() {
let mut failure_node = trie_.get(&node.failure).unwrap();
loop {
if failure_node.length==0 {
break;
}
if failure_node.direct_cnt == 1 {
node.direct_cnt += 1;
}
failure_node = trie_.get(&failure_node.failure).unwrap();
}
}
// 検索
let mut l = 0;
let mut r = 0;
let mut was_failure:bool = true;
let mut current_node:&node;
let mut ans = 0;
loop {
let current_hash = hash(&s[l..r]);
current_node = if let Some(x) = trie.get(¤t_hash) {
x
} else {
&trie[&0]
};
if !was_failure {
ans += current_node.direct_cnt;
}
if l>=s.len() {
break;
}
// 遷移判定
if r==s.len() {
was_failure = true;
l += current_node.length - trie[¤t_node.failure].length;
continue;
}
match (current_node.children[s[r] as usize - b'A' as usize], l==r) {
(true,_) => {
was_failure = false;
r = (r+1).min(s.len());
}
(false,false) => {
was_failure = true;
l += current_node.length - trie[¤t_node.failure].length;
},
(false,true) => {
was_failure = true;
r+=1;
l+=1;
},
}
}
write!(out, "{}", ans).unwrap();
}