結果

問題 No.2867 NOT FOUND 404 Again
コンテスト
ユーザー hayatroid
提出日時 2026-08-25 08:44:54
言語 Rust
(1.97.1 + proconio + num + itertools)
コンパイル:
/usr/bin/rustc_custom
実行:
./target/release/main
結果
AC  
実行時間 2,863 ms / 3,000 ms
+ 664µs
コード長 3,676 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 4,909 ms
コンパイル使用メモリ 195,700 KB
実行使用メモリ 6,272 KB
最終ジャッジ日時 2026-08-25 08:45:53
合計ジャッジ時間 50,377 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 2
other AC * 18
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

use std::{cmp::Ordering, collections::HashMap, hash::Hash};

use proconio::{input, marker::Bytes};

// 0. 決定性有限オートマトン
trait Dfa {
    type State;
    type Alphabet;
    fn init(&self) -> Self::State;
    fn next(&self, q: &Self::State, c: &Self::Alphabet) -> Self::State;
    fn accept(&self, q: &Self::State) -> bool;
}

// 1. Contains(w) は w がつく数を認識するオートマトン
struct Contains {
    next: Vec<[usize; 10]>,
    accept: Vec<bool>,
}
impl Contains {
    fn new(w: &[u8]) -> Self {
        let n = w.len();
        let mut next = vec![[0; 10]; n + 1];
        let mut accept = vec![false; n + 1];
        let mut fail = vec![0; n + 1];
        for q in 0..n {
            next[q] = next[fail[q]];
            let c = (w[q] - b'0') as usize;
            fail[q + 1] = next[q][c];
            next[q][c] = q + 1;
        }
        next[n] = [n; 10];
        accept[n] = true;
        Contains { next, accept }
    }
}
impl Dfa for Contains {
    type State = usize;
    type Alphabet = u8;
    fn init(&self) -> Self::State {
        0
    }
    fn next(&self, q: &Self::State, c: &Self::Alphabet) -> Self::State {
        self.next[*q][(c - b'0') as usize]
    }
    fn accept(&self, q: &Self::State) -> bool {
        self.accept[*q]
    }
}

// 2. Not(a) は a が認識しない数を認識するオートマトン
struct Not<A>(A);
impl<A: Dfa> Dfa for Not<A> {
    type State = A::State;
    type Alphabet = A::Alphabet;
    fn init(&self) -> Self::State {
        self.0.init()
    }
    fn next(&self, q: &Self::State, c: &Self::Alphabet) -> Self::State {
        self.0.next(q, c)
    }
    fn accept(&self, q: &Self::State) -> bool {
        !self.0.accept(q)
    }
}

// 3. And(a, b) は a と b がともに認識する数を認識するオートマトン
struct And<A, B>(A, B);
impl<A: Dfa, B: Dfa<Alphabet = A::Alphabet>> Dfa for And<A, B> {
    type State = (A::State, B::State);
    type Alphabet = A::Alphabet;
    fn init(&self) -> Self::State {
        (self.0.init(), self.1.init())
    }
    fn next(&self, q: &Self::State, c: &Self::Alphabet) -> Self::State {
        (self.0.next(&q.0, c), self.1.next(&q.1, c))
    }
    fn accept(&self, q: &Self::State) -> bool {
        self.0.accept(&q.0) && self.1.accept(&q.1)
    }
}

// 4. Le(n) は n 以下の数を認識するオートマトン
struct Le<'a>(&'a [u8]);
impl Dfa for Le<'_> {
    type State = (Ordering, usize);
    type Alphabet = u8;
    fn init(&self) -> Self::State {
        (Ordering::Equal, 0)
    }
    fn next(&self, q: &Self::State, c: &Self::Alphabet) -> Self::State {
        (q.0.then(c.cmp(&self.0[q.1])), q.1 + 1)
    }
    fn accept(&self, q: &Self::State) -> bool {
        q.0.is_le()
    }
}

const MOD: u64 = 998_244_353;

// 5. count(a, Σ, n) は a が認識する言語と Σ^n の共通部分を 998244353 で割った余りで数える
fn count<A, S>(dfa: A, sigma: S, len: usize) -> u64
where
    A: Dfa,
    A::State: Eq + Hash,
    S: Iterator<Item = A::Alphabet> + Clone,
{
    let mut dp = HashMap::new();
    dp.insert(dfa.init(), 1);
    for _ in 0..len {
        let mut ndp = HashMap::new();
        for (q, v) in dp {
            for c in sigma.clone() {
                let e = ndp.entry(dfa.next(&q, &c)).or_insert(0);
                *e = (*e + v) % MOD;
            }
        }
        dp = ndp;
    }
    dp.iter()
        .filter_map(|(q, v)| dfa.accept(q).then_some(v))
        .fold(0, |sum, v| (sum + v) % MOD)
}

// 6. Just Do It !!!!
fn main() {
    input!(n: Bytes);
    let dfa = And(Not(Contains::new(b"404")), Le(&n));
    println!("{}", (count(dfa, b'0'..=b'9', n.len()) + MOD - 1) % MOD);
}
0