From 6b52466415ed4b46ede6142fbb164b2c40a80db5 Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi Date: Thu, 20 Nov 2025 18:57:01 -0800 Subject: [PATCH 1/5] Add Abbreviation Dynamic Programming Algorithm --- DIRECTORY.md | 1 + src/dynamic_programming/abbreviation.rs | 115 ++++++++++++++++++++++++ src/dynamic_programming/mod.rs | 2 + 3 files changed, 118 insertions(+) create mode 100644 src/dynamic_programming/abbreviation.rs diff --git a/DIRECTORY.md b/DIRECTORY.md index 46bb2a3af9f..33e62facc89 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -87,6 +87,7 @@ * [Union Find](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/union_find.rs) * [Veb Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/veb_tree.rs) * Dynamic Programming + * [Abbreviation](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/abbreviation.rs) * [Coin Change](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/coin_change.rs) * [Egg Dropping](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/egg_dropping.rs) * [Fibonacci](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/fibonacci.rs) diff --git a/src/dynamic_programming/abbreviation.rs b/src/dynamic_programming/abbreviation.rs new file mode 100644 index 00000000000..18c05c911a4 --- /dev/null +++ b/src/dynamic_programming/abbreviation.rs @@ -0,0 +1,115 @@ +//! Abbreviation Problem Solution +//! +//! This module solves the abbreviation problem: determining if string `a` can be +//! transformed into string `b` by capitalizing zero or more lowercase letters and +//! deleting all remaining lowercase letters. + +/// Determines if string `a` can be transformed into string `b` by: +/// 1. Capitalizing zero or more lowercase letters in `a` +/// 2. Deleting all remaining lowercase letters +/// +/// The solution uses dynamic programming where `dp[i][j]` represents whether +/// the first `i` characters of `a` can form the first `j` characters of `b`. +/// +/// # Arguments +/// * `a` - The input string that may contain both uppercase and lowercase letters +/// * `b` - The target string containing only uppercase letters +/// +/// # Returns +/// * A boolean indicating whether the transformation is possible +/// +/// # Complexity +/// * Time complexity: O(n * m) where n is length of string a and m is length of string b +/// * Space complexity: O(n * m) for the DP table +/// +/// # Examples +/// ``` +/// use the_algorithms_rust::dynamic_programming::abbreviation; +/// +/// assert_eq!(abbreviation("daBcd", "ABC"), true); +/// assert_eq!(abbreviation("dBcd", "ABC"), false); +/// ``` +pub fn abbreviation(a: &str, b: &str) -> bool { + let a_chars: Vec = a.chars().collect(); + let b_chars: Vec = b.chars().collect(); + let n = a_chars.len(); + let m = b_chars.len(); + + // dp[i][j] represents whether first i chars of a can form first j chars of b + let mut dp = vec![vec![false; m + 1]; n + 1]; + + // Base case: empty string a can form empty string b + dp[0][0] = true; + + // Fill the first column: we can form empty b by deleting all lowercase letters + for i in 0..n { + if a_chars[i].is_lowercase() { + dp[i + 1][0] = dp[i][0]; + } + } + + for i in 0..n { + for j in 0..=m { + if dp[i][j] { + // If we can match current position, check next characters + if j < m && a_chars[i].to_ascii_uppercase() == b_chars[j] { + dp[i + 1][j + 1] = true; + } + + // If current character in a is lowercase, we can delete it + if a_chars[i].is_lowercase() { + dp[i + 1][j] = true; + } + } + } + } + + dp[n][m] +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! abbreviation_tests { + ($($name:ident: ($a:expr, $b:expr) => $expected:expr,)*) => { + $( + #[test] + fn $name() { + assert_eq!(abbreviation($a, $b), $expected); + } + )* + }; + } + + abbreviation_tests! { + // Original test cases from the problem + test_daBcd_ABC: ("daBcd", "ABC") => true, + test_dBcd_ABC: ("dBcd", "ABC") => false, + test_AbcE_ABE: ("AbcE", "ABE") => true, + test_AbcE_ABC: ("AbcE", "ABC") => false, + test_abcde_ABCDE: ("abcde", "ABCDE") => true, + test_abcde_ABCD: ("abcde", "ABCD") => false, + test_ABCDE_ABCDE: ("ABCDE", "ABCDE") => true, + test_ABCDE_ABCD: ("ABCDE", "ABCD") => false, + test_aBcDe_ABCDE: ("aBcDe", "ABCDE") => true, + test_aBcDe_ABCD: ("aBcDe", "ABCD") => true, + + // Edge test cases + test_empty_both: ("", "") => true, + test_empty_a: ("", "ABC") => false, + test_empty_b: ("abc", "") => true, + test_only_lowercase: ("abc", "ABC") => true, + test_only_uppercase: ("ABC", "ABC") => true, + test_mismatched_uppercase: ("ABD", "ABC") => false, + + // Complex cases from HackerRank + test_complex_1: ("LLZOSYAMQRMBTZXTQMQcKGLR", "LLZOSYAMBTZXMQKLR") => false, + test_complex_2: ("MGYXKOVSMAHKOLAZZKWXKS", "MGXKOVSAHKOLZKKDP") => false, + test_complex_3: ("bfBQZcnjXPMNWMZ", "BQZCNJXPMNWMZ") => true, + test_AbcDE_ABDE: ("AbcDE", "ABDE") => true, + test_AbcDE_AFDE: ("AbcDE", "AFDE") => false, + test_ABCD_ABCD: ("ABCD", "ABCD") => true, + test_abcdE_ABCDE: ("abcdE", "ABCDE") => true + } +} diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index f18c1847479..35937569f88 100644 --- a/src/dynamic_programming/mod.rs +++ b/src/dynamic_programming/mod.rs @@ -1,3 +1,4 @@ +mod abbreviation; mod coin_change; mod egg_dropping; mod fibonacci; @@ -19,6 +20,7 @@ mod subset_generation; mod trapped_rainwater; mod word_break; +pub use self::abbreviation::abbreviation; pub use self::coin_change::coin_change; pub use self::egg_dropping::egg_drop; pub use self::fibonacci::binary_lifting_fibonacci; From 09e043baa1172e5b17a24db4cdc068194ac28c28 Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:09:12 -0800 Subject: [PATCH 2/5] Fix missing comma --- src/dynamic_programming/abbreviation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dynamic_programming/abbreviation.rs b/src/dynamic_programming/abbreviation.rs index 18c05c911a4..46c75031be5 100644 --- a/src/dynamic_programming/abbreviation.rs +++ b/src/dynamic_programming/abbreviation.rs @@ -110,6 +110,6 @@ mod tests { test_AbcDE_ABDE: ("AbcDE", "ABDE") => true, test_AbcDE_AFDE: ("AbcDE", "AFDE") => false, test_ABCD_ABCD: ("ABCD", "ABCD") => true, - test_abcdE_ABCDE: ("abcdE", "ABCDE") => true + test_abcdE_ABCDE: ("abcdE", "ABCDE") => true, } } From f3b79f4aec15ec3b5fbdfbeb4cdcdc3a458e026f Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:12:58 -0800 Subject: [PATCH 3/5] Update abbreviation.rs --- src/dynamic_programming/abbreviation.rs | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/dynamic_programming/abbreviation.rs b/src/dynamic_programming/abbreviation.rs index 46c75031be5..38acaecd75e 100644 --- a/src/dynamic_programming/abbreviation.rs +++ b/src/dynamic_programming/abbreviation.rs @@ -1,14 +1,14 @@ //! Abbreviation Problem Solution //! -//! This module solves the abbreviation problem: determining if string `a` can be -//! transformed into string `b` by capitalizing zero or more lowercase letters and +//! This module solves the abbreviation problem: determining if string `a` can be +//! transformed into string `b` by capitalizing zero or more lowercase letters and //! deleting all remaining lowercase letters. /// Determines if string `a` can be transformed into string `b` by: /// 1. Capitalizing zero or more lowercase letters in `a` /// 2. Deleting all remaining lowercase letters /// -/// The solution uses dynamic programming where `dp[i][j]` represents whether +/// The solution uses dynamic programming where `dp[i][j]` represents whether /// the first `i` characters of `a` can form the first `j` characters of `b`. /// /// # Arguments @@ -25,7 +25,7 @@ /// # Examples /// ``` /// use the_algorithms_rust::dynamic_programming::abbreviation; -/// +/// /// assert_eq!(abbreviation("daBcd", "ABC"), true); /// assert_eq!(abbreviation("dBcd", "ABC"), false); /// ``` @@ -34,20 +34,20 @@ pub fn abbreviation(a: &str, b: &str) -> bool { let b_chars: Vec = b.chars().collect(); let n = a_chars.len(); let m = b_chars.len(); - + // dp[i][j] represents whether first i chars of a can form first j chars of b let mut dp = vec![vec![false; m + 1]; n + 1]; - + // Base case: empty string a can form empty string b dp[0][0] = true; - + // Fill the first column: we can form empty b by deleting all lowercase letters for i in 0..n { if a_chars[i].is_lowercase() { dp[i + 1][0] = dp[i][0]; } } - + for i in 0..n { for j in 0..=m { if dp[i][j] { @@ -55,7 +55,7 @@ pub fn abbreviation(a: &str, b: &str) -> bool { if j < m && a_chars[i].to_ascii_uppercase() == b_chars[j] { dp[i + 1][j + 1] = true; } - + // If current character in a is lowercase, we can delete it if a_chars[i].is_lowercase() { dp[i + 1][j] = true; @@ -63,7 +63,7 @@ pub fn abbreviation(a: &str, b: &str) -> bool { } } } - + dp[n][m] } @@ -86,6 +86,8 @@ mod tests { // Original test cases from the problem test_daBcd_ABC: ("daBcd", "ABC") => true, test_dBcd_ABC: ("dBcd", "ABC") => false, + + // Additional test cases test_AbcE_ABE: ("AbcE", "ABE") => true, test_AbcE_ABC: ("AbcE", "ABC") => false, test_abcde_ABCDE: ("abcde", "ABCDE") => true, @@ -94,7 +96,7 @@ mod tests { test_ABCDE_ABCD: ("ABCDE", "ABCD") => false, test_aBcDe_ABCDE: ("aBcDe", "ABCDE") => true, test_aBcDe_ABCD: ("aBcDe", "ABCD") => true, - + // Edge test cases test_empty_both: ("", "") => true, test_empty_a: ("", "ABC") => false, @@ -102,7 +104,7 @@ mod tests { test_only_lowercase: ("abc", "ABC") => true, test_only_uppercase: ("ABC", "ABC") => true, test_mismatched_uppercase: ("ABD", "ABC") => false, - + // Complex cases from HackerRank test_complex_1: ("LLZOSYAMQRMBTZXTQMQcKGLR", "LLZOSYAMBTZXMQKLR") => false, test_complex_2: ("MGYXKOVSMAHKOLAZZKWXKS", "MGXKOVSAHKOLZKKDP") => false, From 857c09d84a61e887200c68df0db9352e43f0478b Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:16:02 -0800 Subject: [PATCH 4/5] fix: use snake_case for test function names --- src/dynamic_programming/abbreviation.rs | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/dynamic_programming/abbreviation.rs b/src/dynamic_programming/abbreviation.rs index 38acaecd75e..f5987cde31c 100644 --- a/src/dynamic_programming/abbreviation.rs +++ b/src/dynamic_programming/abbreviation.rs @@ -84,18 +84,18 @@ mod tests { abbreviation_tests! { // Original test cases from the problem - test_daBcd_ABC: ("daBcd", "ABC") => true, - test_dBcd_ABC: ("dBcd", "ABC") => false, + test_dabcd_to_abc: ("daBcd", "ABC") => true, + test_dbcd_to_abc: ("dBcd", "ABC") => false, // Additional test cases - test_AbcE_ABE: ("AbcE", "ABE") => true, - test_AbcE_ABC: ("AbcE", "ABC") => false, - test_abcde_ABCDE: ("abcde", "ABCDE") => true, - test_abcde_ABCD: ("abcde", "ABCD") => false, - test_ABCDE_ABCDE: ("ABCDE", "ABCDE") => true, - test_ABCDE_ABCD: ("ABCDE", "ABCD") => false, - test_aBcDe_ABCDE: ("aBcDe", "ABCDE") => true, - test_aBcDe_ABCD: ("aBcDe", "ABCD") => true, + test_abce_to_abe: ("AbcE", "ABE") => true, + test_abce_to_abc: ("AbcE", "ABC") => false, + test_lowercase_abcde_to_abcde: ("abcde", "ABCDE") => true, + test_lowercase_abcde_to_abcd: ("abcde", "ABCD") => false, + test_uppercase_abcde_to_abcde: ("ABCDE", "ABCDE") => true, + test_uppercase_abcde_to_abcd: ("ABCDE", "ABCD") => false, + test_mixed_abcde_to_abcde: ("aBcDe", "ABCDE") => true, + test_mixed_abcde_to_abcd: ("aBcDe", "ABCD") => true, // Edge test cases test_empty_both: ("", "") => true, @@ -109,9 +109,9 @@ mod tests { test_complex_1: ("LLZOSYAMQRMBTZXTQMQcKGLR", "LLZOSYAMBTZXMQKLR") => false, test_complex_2: ("MGYXKOVSMAHKOLAZZKWXKS", "MGXKOVSAHKOLZKKDP") => false, test_complex_3: ("bfBQZcnjXPMNWMZ", "BQZCNJXPMNWMZ") => true, - test_AbcDE_ABDE: ("AbcDE", "ABDE") => true, - test_AbcDE_AFDE: ("AbcDE", "AFDE") => false, - test_ABCD_ABCD: ("ABCD", "ABCD") => true, - test_abcdE_ABCDE: ("abcdE", "ABCDE") => true, + test_abcde_to_abde: ("AbcDE", "ABDE") => true, + test_abcde_to_afde: ("AbcDE", "AFDE") => false, + test_abcd_to_abcd: ("ABCD", "ABCD") => true, + test_abcde_with_trailing_e: ("abcdE", "ABCDE") => true, } } From d92d27388ad655ec3a88f5e99dc66870dcbde6c1 Mon Sep 17 00:00:00 2001 From: Ali Alimohammadi <41567902+AliAlimohammadi@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:22:48 -0800 Subject: [PATCH 5/5] fix: correct test expectation for lowercase_abcde_to_abcd --- src/dynamic_programming/abbreviation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dynamic_programming/abbreviation.rs b/src/dynamic_programming/abbreviation.rs index f5987cde31c..ccfef405095 100644 --- a/src/dynamic_programming/abbreviation.rs +++ b/src/dynamic_programming/abbreviation.rs @@ -91,7 +91,7 @@ mod tests { test_abce_to_abe: ("AbcE", "ABE") => true, test_abce_to_abc: ("AbcE", "ABC") => false, test_lowercase_abcde_to_abcde: ("abcde", "ABCDE") => true, - test_lowercase_abcde_to_abcd: ("abcde", "ABCD") => false, + test_lowercase_abcde_to_abcd: ("abcde", "ABCD") => true, test_uppercase_abcde_to_abcde: ("ABCDE", "ABCDE") => true, test_uppercase_abcde_to_abcd: ("ABCDE", "ABCD") => false, test_mixed_abcde_to_abcde: ("aBcDe", "ABCDE") => true,