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. 开始你的表演
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
}
}