diff --git a/src/lib.rs b/src/lib.rs index c2d7748c..6033121a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1662,6 +1662,7 @@ pub mod problem_2222_number_of_ways_to_select_buildings; pub mod problem_2224_minimum_number_of_operations_to_convert_time; pub mod problem_2225_find_players_with_zero_or_one_losses; pub mod problem_2226_maximum_candies_allocated_to_k_children; +pub mod problem_2227_encrypt_and_decrypt_strings; pub mod problem_2231_largest_number_after_digit_swaps_by_parity; pub mod problem_2232_minimize_result_by_adding_parentheses_to_expression; pub mod problem_2233_maximum_product_after_k_increments; diff --git a/src/problem_2227_encrypt_and_decrypt_strings/hash_map.rs b/src/problem_2227_encrypt_and_decrypt_strings/hash_map.rs new file mode 100644 index 00000000..9c906ec5 --- /dev/null +++ b/src/problem_2227_encrypt_and_decrypt_strings/hash_map.rs @@ -0,0 +1,85 @@ +// ------------------------------------------------------ snip ------------------------------------------------------ // + +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +pub struct Encrypter { + encryption_map: [[u8; 2]; 26], + encrypted_dictionary: HashMap, u8>, +} + +impl Encrypter { + fn new(keys: Vec, values: Vec, dictionary: Vec) -> Self { + let mut encryption_map = [[u8::MAX; 2]; 26]; + + keys.into_iter().zip(values).for_each(|(key, value)| { + encryption_map[key as usize - usize::from(b'a')] = value.into_bytes().try_into().ok().unwrap(); + }); + + let mut encrypted_dictionary = HashMap::with_capacity(dictionary.len()); + + for word in dictionary { + let encrypted_word = Self::encrypt_impl(&encryption_map, word); + + if !encrypted_word.is_empty() { + match encrypted_dictionary.entry(encrypted_word.into_bytes().into_boxed_slice()) { + Entry::Occupied(occupied_entry) => *occupied_entry.into_mut() += 1, + Entry::Vacant(vacant_entry) => { + vacant_entry.insert(1); + } + } + } + } + + Self { + encryption_map, + encrypted_dictionary, + } + } + + fn encrypt_impl(encryption_map: &[[u8; 2]; 26], word: String) -> String { + String::from_utf8( + word.into_bytes() + .into_iter() + .flat_map(|c| encryption_map[usize::from(c) - usize::from(b'a')]) + .collect(), + ) + .unwrap_or_default() + } + + fn encrypt(&self, word1: String) -> String { + Self::encrypt_impl(&self.encryption_map, word1) + } + + fn decrypt(&self, word2: String) -> i32 { + self.encrypted_dictionary + .get(word2.as_bytes()) + .copied() + .unwrap_or(0) + .into() + } +} + +// ------------------------------------------------------ snip ------------------------------------------------------ // + +impl super::Encrypter for Encrypter { + fn new(keys: Vec, values: Vec, dictionary: Vec) -> Self { + Self::new(keys, values, dictionary) + } + + fn encrypt(&self, word1: String) -> String { + self.encrypt(word1) + } + + fn decrypt(&self, word2: String) -> i32 { + self.decrypt(word2) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_solution() { + super::super::tests::run::(); + } +} diff --git a/src/problem_2227_encrypt_and_decrypt_strings/mod.rs b/src/problem_2227_encrypt_and_decrypt_strings/mod.rs new file mode 100644 index 00000000..d64e5a59 --- /dev/null +++ b/src/problem_2227_encrypt_and_decrypt_strings/mod.rs @@ -0,0 +1,52 @@ +pub mod hash_map; + +pub trait Encrypter { + fn new(keys: Vec, values: Vec, dictionary: Vec) -> Self; + fn encrypt(&self, word1: String) -> String; + fn decrypt(&self, word2: String) -> i32; +} + +#[cfg(test)] +mod tests { + use super::Encrypter; + + enum Operation { + Encrypt(&'static str, &'static str), + Decrypt(&'static str, i32), + } + + pub fn run() { + let test_cases = [ + ( + ( + "abcd", + &["ei", "zf", "ei", "am"] as &[_], + &["abcd", "acbd", "adbc", "badc", "dacb", "cadb", "cbda", "abad"] as &[_], + ), + &[ + Operation::Encrypt("abcd", "eizfeiam"), + Operation::Decrypt("eizfeiam", 2), + ] as &[_], + ), + ( + ("b", &["ca"], &["aaa", "cacbc", "bbaba", "bb"]), + &[Operation::Encrypt("bbb", "cacaca"), Operation::Decrypt("cacaca", 0)], + ), + ]; + + for ((keys, values, dictionary), operations) in test_cases { + let encrypter = E::new( + keys.chars().collect(), + values.iter().copied().map(str::to_string).collect(), + dictionary.iter().copied().map(str::to_string).collect(), + ); + + for operation in operations { + match *operation { + Operation::Encrypt(word1, expected) => assert_eq!(encrypter.encrypt(word1.to_string()), expected), + Operation::Decrypt(word2, expected) => assert_eq!(encrypter.decrypt(word2.to_string()), expected), + } + } + } + } +}