Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Last active April 11, 2018 16:23
Show Gist options
  • Save AnthonyMikh/b2b7a5727148f4f64965c2f0b0914d88 to your computer and use it in GitHub Desktop.
Save AnthonyMikh/b2b7a5727148f4f64965c2f0b0914d88 to your computer and use it in GitHub Desktop.
Решение задачи №84 от UniLecs
//Превратить переполнение числовых литералов в ошибку компиляции
#![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));
}
@AnthonyMikh
Copy link
Author

AnthonyMikh commented Apr 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment