Last active
April 11, 2018 16:23
-
-
Save AnthonyMikh/b2b7a5727148f4f64965c2f0b0914d88 to your computer and use it in GitHub Desktop.
Решение задачи №84 от UniLecs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Превратить переполнение числовых литералов в ошибку компиляции | |
#![deny(overflowing_literals)] | |
//Тип для подсчёта значений | |
type Count = u32; | |
//Возвращает факториал аргумента | |
fn factorial(n: Count) -> Count { | |
(1..=n).product() | |
} | |
//Ограничение на входную строку | |
const INPUT_LIMIT: Count = 10; | |
#[derive(Debug, PartialEq)] | |
//Тип возможной ошибки при решении задачи | |
enum AnagramsError { | |
//Переданная строка слишком длинная | |
TooLong, | |
} | |
//Возвращает число анаграмм, которые можно получить из | |
//переданной строки, включая саму строку, или ошибку, | |
//если строка слишком длинная. | |
fn count_anagrams(input: &str) -> Result<Count, AnagramsError> { | |
//Данная задача эквивалентна комбинаторной задаче | |
//об определении числа перестановок с повторениями. | |
//Если среди объектов объект под номером i | |
//повторяется ki раз, то всего различных перестановок | |
//P(k1, k2, ..., kn) = (k1 + k2 + ... + kn)! / k1! * k2! * ... * kn! | |
use std::collections::HashMap; | |
let mut buf = HashMap::new(); | |
let mut chars_len = 0; | |
for c in input.chars() { //Для каждого символа строки | |
*buf.entry(c).or_insert(0) += 1; //инкрементируем кол-во вхождений | |
chars_len += 1; //и длину. | |
if chars_len > INPUT_LIMIT { //Если длина больше заданного лимита | |
return Err(AnagramsError::TooLong); //возвращаем ошибку | |
} | |
} | |
//Считаем факториал длины и последовательно делим | |
//на факториалы количеств вхождения каждого символа | |
let res = buf.into_iter().fold(factorial(chars_len), |acc, (_, count)| { | |
acc / factorial(count) | |
}); | |
Ok(res) | |
} | |
//-------------------------- Тесты -------------------------- | |
#[test] | |
//Убеждаемся, что тип Count вмещает факториал от INPUT_LIMIT. | |
//Если этот тест падает, то решение некорректно | |
fn ensure_count_type_correctness() { | |
let mut acc: Count = 1; | |
for i in 1..=INPUT_LIMIT { | |
//Умножаем и получаем флаг переполнения | |
let (new_acc, overflow) = acc.overflowing_mul(i); | |
assert!(!overflow, "Count type can not handle factorial of {}", i); | |
acc = new_acc; | |
} | |
} | |
#[test] | |
//Проверка некоторых избранных примеров | |
fn check_some_samples() { | |
assert_eq!(count_anagrams("sample"), Ok(720)); | |
assert_eq!(count_anagrams("sammmm"), Ok(30)); | |
assert_eq!(count_anagrams("КОМП"), Ok(24)); | |
assert_eq!(count_anagrams("СОЛО"), Ok(12)); | |
assert_eq!(count_anagrams("0123456789"), Ok(3_628_800)); | |
} | |
#[test] | |
//Проверяем корректную обработку слишком длинных строк | |
fn check_error() { | |
const LONG_STRING_LEN: Count = INPUT_LIMIT + 1; | |
let mut long_string = String::new(); | |
for _ in 0..LONG_STRING_LEN { | |
long_string.push('A'); | |
} | |
assert_eq!(count_anagrams(&long_string), Err(AnagramsError::TooLong)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Playground rev. 1: https://play.rust-lang.org/?gist=4fb5326f71c4f3aaa8472919000798c7&version=nightly
Playground rev. 3: https://play.rust-lang.org/?gist=2fd713a83c10b000547472ba996701b8&version=nightly