use crate::Solution;
use itertools::Itertools;
use pmath::digits::{digits, is_permutation};
problem!(Problem0098, 98, "Anagramic Squares");
impl Solution for Problem0098 {
fn solve(&self) -> String {
let input_str = include_str!("0098_words.txt");
let mut words_vec: Vec<(&str, Vec<char>)> = input_str
.split(',')
.map(|word| {
let word = word.trim_matches('"');
let mut word_chars: Vec<char> = word.chars().collect();
word_chars.sort();
(word, word_chars)
})
.collect();
let mut anagram_pairs = Vec::new();
while let Some((word_str, word_chars)) = words_vec.pop() {
let mut anagram_group = vec![word_str];
let mut i = 0;
while i < words_vec.len() {
if word_chars == words_vec[i].1 {
anagram_group.push(words_vec[i].0);
words_vec.remove(i);
} else {
i += 1;
}
}
for combination in anagram_group.into_iter().combinations(2) {
anagram_pairs.push((combination[0], combination[1]));
}
}
anagram_pairs.sort_by_key(|pair| pair.0.len());
assert!(anagram_pairs.last().unwrap().0.len() <= 10);
let mut maximum: u64 = 0;
while let Some(word_pair) = anagram_pairs.pop() {
if maximum != 0 && word_pair.0.len() < maximum.ilog10() as usize + 1 {
break;
}
let (word1, word2) = (word_pair.0, word_pair.1);
let (mut storage1, mut storage2) = (Vec::new(), Vec::new());
let squares_iter = (((10_u64.pow(word1.len() as u32 - 1) as f64).sqrt().ceil() as u64)
..=((10_u64.pow(word1.len() as u32) as f64).sqrt().floor() as u64))
.map(|i| i * i);
for square in squares_iter {
let match_1 = word_matches_num(word1, square);
if match_1.0 {
storage1.push((square, match_1.1));
}
let match_2 = word_matches_num(word2, square);
if match_2.0 {
storage2.push((square, match_2.1));
}
}
for (sqr1, arr1) in storage1 {
for (sqr2, arr2) in &storage2 {
if is_permutation(sqr1, *sqr2, 10) && arr1 == *arr2 {
maximum = maximum.max(sqr1).max(*sqr2);
}
}
}
}
maximum.to_string()
}
}
fn word_matches_num(word: &str, num: u64) -> (bool, [char; 10]) {
let mut num_char_dict = [' '; 10];
for (n, c) in digits(num, 10).rev().zip(word.chars()) {
if num_char_dict[n as usize] == ' ' {
num_char_dict[n as usize] = c;
} else if num_char_dict[n as usize] != c {
return (false, num_char_dict);
}
}
for c in num_char_dict.iter() {
if *c != ' ' && num_char_dict.iter().filter(|&x| *x == *c).count() > 1 {
return (false, num_char_dict);
}
}
(true, num_char_dict)
}