LoginSignup
3
2

More than 5 years have passed since last update.

オフラインリアルタイムどう書く E24 の実装例(速いやつ, Rust)

Last updated at Posted at 2018-11-18

問題 : http://nabetani.sakura.ne.jp/hena/orde24tancho/
実装リンク集 : https://qiita.com/Nabetani/items/928d6a94d83c21ef64d7

次回のイベントは 12/8。
神田秋葉原界隈です。
詳しくは
https://yhpg.doorkeeper.jp/events/82699
を御覧ください。

で。

https://qiita.com/Nabetani/items/800c127e5ed9cb0ca207 にある、あとの方の実装を rust に移植した。

Rust
extern crate num;

use num::One;
use num::ToPrimitive;
use std::collections::VecDeque;

// 数字に使う文字
const NUMS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";

// 階乗
fn fact(n: i64) -> num::BigInt {
    if n < 2i64 {
        num::BigInt::one()
    } else {
        n * fact(n - 1)
    }
}

// num 種類の文字を使った keta 桁の単調増加数の個数
fn count(n: i64, keta: i64) -> i64 {
    if n < keta {
        0
    } else {
        let r = fact(n) / fact(n - keta) / fact(keta);
        r.to_i64().unwrap()
    }
}

// VecDeque を作るマクロ
macro_rules! vecdeque{
    ( $reserve:expr; $( $x:expr ),* ) => {{
        let mut r  = VecDeque::new();
        r.reserve($reserve as usize);
        $( r.push_back($x); )*
        r
    }}
}

// keta 桁の単調増加数の中で、 t0+1 番目の数の b 進数表現
fn find(b: i64, keta: i64, t0: i64) -> VecDeque<i64> {
    let mut t = t0;
    if keta == 1 {
        return vecdeque![b; t + 1];
    }
    for head in 1.. {
        let tail_d = b - 1 - head;
        let c = count(tail_d, keta - 1);
        if c <= t {
            t -= c;
        } else {
            let tail0 = find(tail_d + 1, keta - 1, t);
            let mut tail = tail0.iter().map(|c| c + head).collect::<VecDeque<i64>>();
            tail.push_front(head);
            return tail;
        }
    }
    panic!("unexpected")
}

// 小さい方から m 番目の「基数bの単調増加数」の VecDeque<i64> 表現
fn solve_impl(b: i64, m: i64) -> Option<VecDeque<i64>> {
    let mut t = m - 1;
    for keta in 1..b {
        let c = count(b - 1, keta);
        if c <= t {
            t -= c;
        } else {
            return Some(find(b, keta, t));
        }
    }
    None
}

// VecDeque で表現されている数字を String に変換する
fn to_text(d: VecDeque<i64>) -> String {
    d.iter()
        .map(|c| (NUMS.as_bytes()[*c as usize] as char).to_string())
        .collect::<Vec<String>>()
        .join("")
}

fn solve(src: &String) -> String {
    let nums = src
        .split(",")
        .map(|e| e.parse::<i64>().unwrap())
        .collect::<VecDeque<i64>>();
    let ans = solve_impl(nums[0], nums[1]);
    match ans {
        Some(x) => to_text(x),
        _ => "-".to_string(),
    }
}

fn test(num: i32, src: String, expected: String) {
    let actual = solve(&src);
    let okay = actual == expected;
    println!(
        "{}, {}, src: {}, act: {}, exp: {}",
        num,
        if okay { "ok" } else { "**NG**" },
        src,
        actual,
        expected
    );
}

macro_rules! test {
    ($n:expr, $x:expr, $y:expr) => {
        test($n, $x.to_string(), $y.to_string());
    };
}

fn main() {
    test!(0, "16,333", "38e");
    test!(1, "2,100", "-");
    test!(2, "2,1", "1");
    test!(3, "2,2", "-");
    // 中略
    test!(56, "36,44811", "abpu");
}

いつもどおりテストの大半は省略。

find 内で使っているコンテナ。先頭への挿入が発生するので Vec ではなく VecDeque にしてみた。
それに伴い、無駄にマクロを用意してみた。

rubyで書いていたときい享受していた多倍長整数の恩恵を、rust でも享受すべく、num::BigInt を使ってみた。ありがたい。
C++ には標準の多倍長整数がないので、これは私にとって rust の大きなアドバンテージになる。すばらしい。
あと。
i128 があることを発見した。これも素晴らしい。
今回は 36! ≒ 2**138 なので利用できなかったけど。

今回の最大の不満は to_text
上記の実装では VecDeque<i64> しか受け取れないが、Generics を使って、Vec<i64>VecDeque<i32> 、あるいは Vec<i32> も受け取れるように書きたかった。
が。
書き方がわからなかった。悔しい。

関数の中身

Rust
    d.iter()
        .map(|c| (NUMS.as_bytes()[*c as usize] as char).to_string())
        .collect::<Vec<String>>()
        .join("")

は、Vec<i32> でも VecDeque<i32> でも関係ないように書けているつもり。
宣言の仕方がわからなかった。

あと。
マクロ楽しいね。ちょっと怖いけど。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2