diff --git a/DIRECTORY.md b/DIRECTORY.md index e6a35c9e71b..aa20c6f86a0 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..ccfef405095 --- /dev/null +++ b/src/dynamic_programming/abbreviation.rs @@ -0,0 +1,117 @@ +//! 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_to_abc: ("daBcd", "ABC") => true, + test_dbcd_to_abc: ("dBcd", "ABC") => false, + + // Additional test cases + 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") => 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, + test_mixed_abcde_to_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_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, + } +} diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index 2d350c6a1d2..c5adb434a8c 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; @@ -20,6 +21,7 @@ mod subset_sum; 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;