Alphametics
1. Readme
算术谜题
写一个函数来解决字母谜题.
Alphametics是一个拼图,其中单词中的字母被数字替换.
例如SEND + MORE = MONEY
:
S E N D
M O R E +
-----------
M O N E Y
用有效数字替换它们会给出:
9 5 6 7
1 0 8 5 +
-----------
1 0 6 5 2
这是正确的,因为每个字母都被不同的数字替换,之后单词会被翻译成数字,然后产生有效的总和。
每个字母必须代表不同的数字,并且多位数的首数字不能是 0 。
写一个函数来解决字母谜题.
2. 开始你的表演
use std::collections::HashMap; pub fn solve(input: &str) -> Option<HashMap<char, u8>> { unimplemented!("Solve the alphametic {:?}", input) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { // use std::collections::HashMap; fn assert_alphametic_solution_eq(puzzle: &str, solution: &[(char, u8)]) { let answer = solve(puzzle); let solution: HashMap<char, u8> = solution.iter().cloned().collect(); assert_eq!(answer, Some(solution)); } #[test] fn test_with_three_letters() { assert_alphametic_solution_eq("I + BB == ILL", &[('I', 1), ('B', 9), ('L', 0)]); } #[test] //#[ignore] fn test_must_have_unique_value_for_each_letter() { let answer = solve("A == B"); assert_eq!(answer, None); } #[test] //#[ignore] fn test_leading_zero_solution_is_invalid() { let answer = solve("ACA + DD == BD"); assert_eq!(answer, None); } #[test] //#[ignore] fn test_puzzle_with_four_letters() { assert_alphametic_solution_eq("AS + A == MOM", &[('A', 9), ('S', 2), ('M', 1), ('O', 0)]); } #[test] //#[ignore] fn test_puzzle_with_six_letters() { assert_alphametic_solution_eq( "NO + NO + TOO == LATE", &[('N', 7), ('O', 4), ('T', 9), ('L', 1), ('A', 0), ('E', 2)], ); } #[test] //#[ignore] fn test_puzzle_with_seven_letters() { assert_alphametic_solution_eq( "HE + SEES + THE == LIGHT", &[ ('E', 4), ('G', 2), ('H', 5), ('I', 0), ('L', 1), ('S', 9), ('T', 7), ], ); } #[test] //#[ignore] fn test_puzzle_with_eight_letters() { assert_alphametic_solution_eq( "SEND + MORE == MONEY", &[ ('S', 9), ('E', 5), ('N', 6), ('D', 7), ('M', 1), ('O', 0), ('R', 8), ('Y', 2), ], ); } #[test] //#[ignore] fn test_puzzle_with_ten_letters() { assert_alphametic_solution_eq( "AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE", &[ ('A', 5), ('D', 3), ('E', 4), ('F', 7), ('G', 8), ('N', 0), ('O', 2), ('R', 1), ('S', 6), ('T', 9), ], ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { // This is a brute-force solution, use `cargo test --release` for faster testing extern crate itertools; extern crate permutohedron; use itertools::Itertools; use permutohedron::Heap as Permutations; use std::char; use std::collections::HashMap; use std::collections::HashSet; fn test_equation(puzzle: &str, substitutions: &HashMap<char, u8>) -> bool { // Create a new String with characters changed to numbers let puzzle: String = puzzle .chars() .map(|c| { if let Some(&n) = substitutions.get(&c) { // If the character is in the substitutions, get the number and // convert it to a char char::from_digit(n as u32, 10).unwrap() } else { // Otherwise just copy over the character c } }) .collect(); // Split the puzzle into left and right side let equation: Vec<&str> = puzzle.split("==").collect(); // Parse the number on the right side let right = equation[1].trim().parse::<u32>().unwrap(); // Sum the parts on the left side let left: u32 = equation[0] .split('+') .map(str::trim) .map(|n| n.parse::<u32>().unwrap()) .sum(); // Create a String with just the numbers and spaces let just_numbers = puzzle .chars() .filter(|c| c.is_digit(10) || c.is_whitespace()) .collect::<String>(); // Split this into the numbers and check every number's first character let no_leading_zeroes = just_numbers .split_whitespace() .all(|number| number.chars().next().unwrap() != '0'); // Return true if left and right side is equal and the equation doesnt // contain leading zeroes. left == right && no_leading_zeroes } pub fn solve(puzzle: &str) -> Option<HashMap<char, u8>> { // Get unique letters from the puzzle let letters: HashSet<char> = puzzle .chars() .filter(|&c| c.is_alphabetic() && c.is_uppercase()) .collect(); let letters: Vec<char> = letters.into_iter().collect(); // All available numbers for substitution let numbers: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; // Iterate every combination with the length of unique letters in the puzzle for combinations in numbers.iter().combinations(letters.len()) { let mut c = combinations; let permutations = Permutations::new(&mut c); // Iterate every permutation of a letter combination for p in permutations { let substitution: HashMap<char, u8> = letters.iter().zip(p).map(|(&c, &n)| (c, n)).collect(); if test_equation(puzzle, &substitution) { // We found a good substitution return Some(substitution); } } } // If we tested every combination and did not found a solution then return None None } #}