Problem:
MathJax.Hub.Config({
tex2jax: { inlineMath: [['\\(','\\)']] }
});

Problem L: The Return of FizzBuzz

ICPC World Finals 7日目

いよいよ明日はICPC World Finalsの本番である。
ティー氏はあるオンラインジャッジ(Aru Online Judge)で練習をすることにした。
問題一覧を眺めているとFizzBuzzという問題が目についた。
この問題は、FizzBuzzゲームで得られる発言のn文字目から20文字を出力するというものだ。

…ふぅ。あっという間に解けてしまった。 これでは簡単すぎた。
入力と出力を逆にした問題を作ってみることにしよう。

問題

FizzBuzzとは、1以上の整数を順に、以下のルールに従って発言していくゲームである。

3で割り切れる時には「Fizz」

5で割り切れる時には「Buzz」

3と5の両方で割り切れる時には「FizzBuzz」

それ以外の時はその数字

ゲームの進行状況を以下に示す。

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz,
11, Fizz, 13, 14, FizzBuzz, 16, …

得られた発言を結合することで得られる（無限長の）文字列をFizzBuzz Stringと呼ぶ。
ある文字列\(s\)が与えられる。 \(s\)がFizzBuzz Stringの部分文字列として出現するかを判定し、
出現する場合には最初に出現するインデックスを求めよ。

入力

n
s1
s2
…
sn

入力は複数のテストケースからなる。 1行目にテストケース数\(n\)が与えられる。 2行目から\( n+1
\)行目は各テストケースに対応し、 文字列\( s_{i} \)が1行で与えられる。

出力

\(i\)番目の文字列\( s_{i} \)について、 \( s_{i}
\)がFizzBuzz Stringの部分文字列として出現する場合には最初に出現するインデックスを(1-indexで)、
出現しない場合には"-1"を\(i\)行目に出力せよ。

制約

\( 1 \leq n \leq 20 \)

文字列は文字\( \{ 0,1,\cdots,8,9
,\mbox{F},\mbox{B},\mbox{i},\mbox{u},\mbox{z} \} (1 \leq i \leq n)
\)からなる。

文字列の長さは1以上15以下である。

入出力例

入力1

6
78Fizz
98FizzBuzz101
FizzBu
izzFiz
111111111111111
123456789

出力1

16
304
18
-1
7703703700
7795884765

入力例は6つのテストケースからなる。 それぞれ以下の発言に対応する。

…, Buzz, Fizz, 7, 8, Fizz, Buzz, …

…, Fizz, 97, 98, Fizz, Buzz, 101, Fizz, …

…, 7, 8, Fizz, Buzz, 11, 12, …

存在しない

…, 1111111109, FizzBuzz, 1111111111,
1111111112, Fizz, 1111111114, …

…, 1123456787, Fizz, 1123456789, Buzz, Fizz, …

Solution in Rust (judge verdict: Accepted)

extern crate core;

use std::fmt;
use std::cmp::{Ordering, min, max};
use std::fmt::{Display, Error, Formatter, Binary, Pointer};
use std::f32::MAX;
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut, SubAssign};
use std::collections::{BTreeMap, VecDeque, BinaryHeap, BTreeSet};
fn show<T: Display>(vec: &Vec<T>) {
    if vec.is_empty() {
        println!("[]");
    }else {
        print!("[{}", vec[0]);
        for i in 1 .. vec.len() {
            print!(", {}", vec[i]);
        }
        println!("]");
    }
}
fn show2<T: Display>(vec: &Vec<Vec<T>>) {
    if vec.is_empty() {
        println!("[]");
    }else {
        for l in vec {
            show(l);
        }
    }
}
macro_rules! read_line{
    () => {{
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        line
    }};
    (delimiter: ' ') => {
        read_line!().split_whitespace().map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (delimiter: $p:expr) => {
        read_line!().split($p).map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (' ') => {
        read_line!(delimiter: ' ')
    };
    ($delimiter:expr) => {
        read_line!(delimiter: $delimiter)
    };
    (' '; $ty:ty) => {
        read_line!().split_whitespace().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
    ($delimiter:expr; $ty:ty) => {
        read_line!($delimiter).into_iter().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
}
macro_rules! read_value{
    () => {
        read_line!().trim().parse().ok().unwrap()
    }
}
macro_rules! let_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}
macro_rules! let_mut_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let mut $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}

fn inner_digits(state: &String) -> Option<(usize, usize)> {
    let state = state.chars().collect::<Vec<_>>();
    for left in 1 .. state.len() {
        if state[left - 1].is_alphabetic() && state[left].is_digit(10){
            for right in left .. state.len() - 1{
                if state[right].is_digit(10) && state[right + 1].is_alphabetic() {
                    return Some((left, right + 1))
                }
            }
        }
    }
    None
}
fn left_digits(state: &String) -> Option<usize> {
    let state = state.chars().collect::<Vec<_>>();
    if state[0].is_digit(10) {
        for i in 1..state.len() {
            if !state[i].is_digit(10){
                return Some(i)
            }
        }
    }
    None
}
fn right_digits(state: &String) -> Option<usize> {
    let state = state.chars().collect::<Vec<_>>();
    if state[state.len() - 1].is_digit(10){
        for i in 1 .. state.len() {
            if !state[state.len() - 1 - i].is_digit(10){
                return Some(state.len() - i)
            }
        }
    }
    None
}
fn make_suspects(state: &String, until: usize, from: usize) -> Vec<i64> {
    let suffix = take_number(state, 0, until);
    let prefix = take_number(state, from, state.len());
    let mut result = Vec::new();
    for i in 1 .. 3 {
        for v in make_suspects_from_suffix_and_prefix(suffix, until as i64,prefix, i) {
            result.push(v);
        }
    }
    result.sort();
    result
}
fn bin_end(state: &String) -> Vec<i64> {
    let origin = take_number(state, 0, state.len());
    let mut digit = 1_i64;
    for _ in 0 .. state.len() {
        digit *= 10;
    }
    let mut result = Vec::with_capacity(50);
    for i in 0 .. 10 {
        for j in 0 .. 10 {
            if j % 5 != 0 && j % 3 != 0 {
                result.push((digit * i + origin) * 10 + j);
            }
        }
    }
    result
}
fn make_suspects_from_suffix_and_prefix(suffix: i64, digit: i64, prefix: i64, diff: i64) -> Vec<i64> {
    let mut suffix_digit = 1_i64;
    for _ in 0 .. digit {
        suffix_digit *= 10;
    }
    //println!("suffix: {}, digit: {}", suffix, suffix_digit);
    let mut bias = 1_i64;
    let mut result = Vec::new();
    //println!("suffix: {}, bias: {}, prefix: {}", suffix, bias, prefix);
    while suffix_digit > bias {
        let suspect = (suffix + diff) % bias + bias * prefix;
        //println!("suffix: {}, bias: {}, prefix: {}, suspect: {}", suffix, bias, prefix, suspect);
        if (suspect - diff + suffix_digit) % suffix_digit == suffix {
            result.push(suspect);
        }
        bias *= 10;
    }
    let suspect = (suffix + diff) % bias + bias * prefix;
    //println!("suffix: {}, bias: {}, prefix: {}, suspect: {}", suffix, bias, prefix, suspect);
    if (suspect - diff + suffix_digit) % suffix_digit == suffix {
        result.push(suspect);
    }
    for i in 0 .. 10 {
        let suspect = (suffix + diff) % bias + bias * i + bias * 10 * prefix;
        //println!("suffix: {}, bias: {}, prefix: {}, suspect: {}", suffix, bias, prefix, suspect);
        if (suspect - diff + suffix_digit) % suffix_digit == suffix {
            result.push(suspect);
        }
    }
    result
}
fn make_fizz_buzz(from: i64, length: usize) -> String {
    let mut result = Vec::with_capacity(length);
    for i in from .. from + length as i64{
        if i % 15 == 0 {
            result.push("FizzBuzz".to_string());
        }else if i % 3 == 0 {
            result.push("Fizz".to_string());
        }else if i % 5 == 0 {
            result.push("Buzz".to_string());
        }else {
            result.push(i.to_string());
        }
    }
    result.concat()
}
fn match_position(suspect: &String, original: &String) -> Option<usize> {
    //println!("suspect: {}, original: {}", suspect, original);
    let suspect = suspect.chars().collect::<Vec<_>>();
    let original = original.chars().collect::<Vec<_>>();
    for i in 0 .. suspect.len() - original.len() + 1 {
        let mut can_make = true;
        for j in 0 .. original.len() {
            if suspect[i + j] != original[j] {
                can_make = false;
                break
            }
        }
        if can_make {
            //println!("find: {}", i);
            return Some(i)
        }
    }
    None
}
fn match_index(suspect: i64, original: &String) -> Option<i64> {
    if suspect <= original.len() as i64 {
        match_position(&make_fizz_buzz(1, original.len() * 2), original).map(|x|x as i64 + 1)
    }else {
        if let Some(pos) = match_position(&make_fizz_buzz(suspect - original.len() as i64, original.len() * 2), original) {
            //println!("make fizz buzz: {}, index: {}, fizz buzz: {}", suspect - original.len() as i64, calculate_index(suspect - original.len() as i64), make_fizz_buzz(1, (3 + suspect - original.len() as i64) as usize));
            Some(pos as i64 + 1 + calculate_index(suspect - original.len() as i64))
        }else {
            None
        }
    }
}
fn take_number(state: &String, from: usize, until: usize) -> i64 {
    let state = state.chars().collect::<Vec<_>>();
    let mut result = 0_i64;
    for i in from..until {
        if let Some(d) = state[i].to_digit(10) {
            result *= 10;
            result += d as i64;
        } else {
            unreachable!()
        }
    }
    result
}
fn suspects_from_suffix(state: &String, until: usize) -> Vec<i64> {
    let mut result = Vec::with_capacity(10);
    let suffix = take_number(state, 0, until);
    let mut bias = 1_i64;
    for _ in 0 .. until {
        bias *= 10;
    }
    for i in 0 .. 10 {
        result.push(suffix + i * bias);
    }
    result
}
fn suspects_from_prefix(state: &String, from: usize) -> Vec<i64> {
    let mut result = Vec::with_capacity(11);
    let value = take_number(state, from, state.len());
    result.push(value);
    for i in 0 .. 10 {
        result.push(value * 10 + i);
        for j in 0 .. 10 {
            result.push(value * 100 + i * 10 * j);
        }
    }
    result
}
fn count_non_fizz_buzz_number_under(digit: i64) -> i64 {
    let mut bias = 1_i64;
    for _ in 0 .. digit {
        bias *= 10;
    }
    (bias - 1) - (bias - 1) / 3 - (bias - 1) / 5 + (bias - 1) / 15
}
fn count_non_fizz_buzz_number_just(digit: i64) -> i64 {
    count_non_fizz_buzz_number_under(digit) - count_non_fizz_buzz_number_under(digit - 1)
}
fn calculate_index(from: i64) -> i64 {
    let from = from - 1;
    let mut digit = 1_i64;
    let mut bias = 1_i64;
    let mut non_fizz_buzz_digit = 0_i64;
    while from > bias * 10 {
        non_fizz_buzz_digit += count_non_fizz_buzz_number_just(digit) * digit;
        digit += 1;
        bias *= 10;
    };
    let fizz_buzz = from / 3 + from / 5 - from / 15;
    non_fizz_buzz_digit += (from - (fizz_buzz + count_non_fizz_buzz_number_under(digit - 1))) * digit;
    non_fizz_buzz_digit + (from / 3 + from / 5) * 4
}
fn not_contains_digits(state: &String) -> bool {
    state.chars().all(|c| !c.is_digit(10))
}
fn not_contains_alphabet(state: &String) -> bool { state.chars().all(|c|c.is_digit(10))}
fn solve(state: &String) -> i64 {
    if not_contains_digits(state) {
        match_position(&make_fizz_buzz(1, 16), state).map(|x| x as i64 + 1).unwrap_or(-1)
    }else if not_contains_alphabet(state) {
        let mut suspects = Vec::new();
        for from in 0 .. state.len() {
            for until in from + 1 .. state.len() {
                //println!("suspect: {}", take_number(state, from, until));
                if let Some(pos) = match_index(take_number(state, from, until), state) {
                    suspects.push(pos);
                }
            }
        }
        for i in 1 .. state.len() {
            //println!("bi prefix: {}", take_number(state, 0, i));
            for s in make_suspects(state, i, i) {
                //println!("bi end suspect: {}", s);
                if let Some(pos) = match_index(s, state){
                    suspects.push(pos);
                }
            }
        }
        for s in suspects_from_suffix(state, state.len()) {
            //println!("suffix suspect: {}", s);
            if let Some(pos) = match_index(s, state){
                suspects.push(pos);
            }
        }
        for s in suspects_from_prefix(state, 0){
            //println!("prefix suspect: {}", s);
            if let Some(pos) = match_index(s, state){
                suspects.push(pos);
            }
        }
        for s in bin_end(state) {
            //println!("bin end: {}", s);
            if let Some(pos) = match_index(s, state){
                suspects.push(pos);
            }
        }
        *suspects.iter().min().unwrap_or(&-1_i64)
    }else if let Some((from, until)) = inner_digits(state) {
        for mid in from .. until {
            if let Some(pos) = match_index(take_number(state, from, mid + 1), state) {
                return pos
            }
        }
        -1
    }else {
        let mut suspects = Vec::new();
        for suffix in left_digits(state) {
            for from in 0 .. suffix {
                if let Some(pos) = match_index(take_number(state, from, suffix), state) {
                    suspects.push(pos);
                }
            }
            for s in suspects_from_suffix(state, suffix) {
                if let Some(pos) = match_index(s, state) {
                    suspects.push(pos);
                }
            }
        }
        for prefix in right_digits(state) {
            for until in prefix + 1 .. state.len() {
                if let Some(pos) = match_index(take_number(state, prefix, until), state) {
                    suspects.push(pos);
                }
            }
            for s in suspects_from_prefix(state, prefix) {
                if let Some(pos) = match_index(s, state){
                    suspects.push(pos);
                }
            }
        }
        for suffix in left_digits(state) {
            for prefix in right_digits(state) {
                for s in make_suspects(state, suffix, prefix) {
                    if let Some(pos) = match_index(s, state) {
                        suspects.push(pos);
                    }
                }
            }
        }
        //show(&suspects);
        *suspects.iter().min().unwrap_or(&-1)
    }
}

fn main(){
    let_all!(n: usize);
    for _ in 0 .. n {
        println!("{}", solve(&read_line!().trim().to_string()))
    }
}