diff --git a/DIRECTORY.md b/DIRECTORY.md index 9645d01eda3..934db9477b6 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -110,6 +110,7 @@ * [Maximum Subarray](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/maximum_subarray.rs) * [Minimum Cost Path](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/minimum_cost_path.rs) * [Optimal BST](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/optimal_bst.rs) + * [Palindrome Partitioning](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/palindrome_partitioning.rs) * [Rod Cutting](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/rod_cutting.rs) * [Smith-Waterman](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/smith_waterman.rs) * [Snail](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/snail.rs) @@ -150,7 +151,7 @@ * [Segment](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/segment.rs) * Graph * [Astar](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/astar.rs) - * [Bellman Ford](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bellman_ford.rs) + * [Bellman-Ford](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bellman_ford.rs) * [Bipartite Matching](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bipartite_matching.rs) * [Breadth First Search](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/breadth_first_search.rs) * [Centroid Decomposition](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/centroid_decomposition.rs) @@ -233,12 +234,12 @@ * [Geometric Series](https://github.com/TheAlgorithms/Rust/blob/master/src/math/geometric_series.rs) * [Greatest Common Divisor](https://github.com/TheAlgorithms/Rust/blob/master/src/math/greatest_common_divisor.rs) * [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/math/huber_loss.rs) - * [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/math/infix_to_postfix.rs) + * [Infix to Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/math/infix_to_postfix.rs) * [Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interest.rs) * [Interpolation](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interpolation.rs) * [Interquartile Range](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interquartile_range.rs) * [Karatsuba Multiplication](https://github.com/TheAlgorithms/Rust/blob/master/src/math/karatsuba_multiplication.rs) - * [LCM Of N Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/lcm_of_n_numbers.rs) + * [LCM of N Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/lcm_of_n_numbers.rs) * [Leaky Relu](https://github.com/TheAlgorithms/Rust/blob/master/src/math/leaky_relu.rs) * [Least Square Approx](https://github.com/TheAlgorithms/Rust/blob/master/src/math/least_square_approx.rs) * [Linear Sieve](https://github.com/TheAlgorithms/Rust/blob/master/src/math/linear_sieve.rs) @@ -249,7 +250,7 @@ * [Miller Rabin](https://github.com/TheAlgorithms/Rust/blob/master/src/math/miller_rabin.rs) * [Modular Exponential](https://github.com/TheAlgorithms/Rust/blob/master/src/math/modular_exponential.rs) * [Newton Raphson](https://github.com/TheAlgorithms/Rust/blob/master/src/math/newton_raphson.rs) - * [Nthprime](https://github.com/TheAlgorithms/Rust/blob/master/src/math/nthprime.rs) + * [N-th prime](https://github.com/TheAlgorithms/Rust/blob/master/src/math/nthprime.rs) * [Pascal Triangle](https://github.com/TheAlgorithms/Rust/blob/master/src/math/pascal_triangle.rs) * [Perfect Cube](https://github.com/TheAlgorithms/Rust/blob/master/src/math/perfect_cube.rs) * [Perfect Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/perfect_numbers.rs) diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index b2d3c640f98..7f5de2be42c 100644 --- a/src/dynamic_programming/mod.rs +++ b/src/dynamic_programming/mod.rs @@ -14,6 +14,7 @@ mod maximal_square; mod maximum_subarray; mod minimum_cost_path; mod optimal_bst; +mod palindrome_partitioning; mod rod_cutting; mod smith_waterman; mod snail; @@ -47,6 +48,7 @@ pub use self::maximal_square::maximal_square; pub use self::maximum_subarray::maximum_subarray; pub use self::minimum_cost_path::minimum_cost_path; pub use self::optimal_bst::optimal_search_tree; +pub use self::palindrome_partitioning::minimum_palindrome_partitions; pub use self::rod_cutting::rod_cut; pub use self::smith_waterman::{score_function, smith_waterman, traceback}; pub use self::snail::snail; diff --git a/src/dynamic_programming/palindrome_partitioning.rs b/src/dynamic_programming/palindrome_partitioning.rs new file mode 100644 index 00000000000..4b46bc3f895 --- /dev/null +++ b/src/dynamic_programming/palindrome_partitioning.rs @@ -0,0 +1,130 @@ +/// Finds the minimum cuts needed for a palindrome partitioning of a string +/// +/// Given a string s, partition s such that every substring of the partition is a palindrome. +/// This function returns the minimum number of cuts needed. +/// +/// Time Complexity: O(n^2) +/// Space Complexity: O(n^2) +/// +/// # Arguments +/// +/// * `s` - The input string to partition +/// +/// # Returns +/// +/// The minimum number of cuts needed +/// +/// # Examples +/// +/// ``` +/// use the_algorithms_rust::dynamic_programming::minimum_palindrome_partitions; +/// +/// assert_eq!(minimum_palindrome_partitions("aab"), 1); +/// assert_eq!(minimum_palindrome_partitions("aaa"), 0); +/// assert_eq!(minimum_palindrome_partitions("ababbbabbababa"), 3); +/// ``` +/// +/// # Algorithm Explanation +/// +/// The algorithm uses dynamic programming with two key data structures: +/// - `cut[i]`: minimum cuts needed for substring from index 0 to i +/// - `is_palindromic[j][i]`: whether substring from index j to i is a palindrome +/// +/// For each position i, we check all possible starting positions j to determine +/// if the substring s[j..=i] is a palindrome. If it is, we update the minimum +/// cut count accordingly. +/// +/// Reference: +pub fn minimum_palindrome_partitions(s: &str) -> usize { + let chars: Vec = s.chars().collect(); + let length = chars.len(); + + if length == 0 { + return 0; + } + + // cut[i] represents the minimum cuts needed for substring from 0 to i + let mut cut = vec![0; length]; + + // is_palindromic[j][i] represents whether substring from j to i is a palindrome + let mut is_palindromic = vec![vec![false; length]; length]; + + for i in 0..length { + let mut mincut = i; + + for j in 0..=i { + // Check if substring from j to i is a palindrome + // A substring is a palindrome if: + // 1. The characters at both ends match (chars[i] == chars[j]) + // 2. AND either: + // - The substring length is less than 2 (single char or two same chars) + // - OR the inner substring (j+1 to i-1) is also a palindrome + if chars[i] == chars[j] && (i - j < 2 || is_palindromic[j + 1][i - 1]) { + is_palindromic[j][i] = true; + mincut = if j == 0 { + // If the entire substring from 0 to i is a palindrome, no cuts needed + 0 + } else { + // Otherwise, take minimum of current mincut and (cuts up to j-1) + 1 + mincut.min(cut[j - 1] + 1) + }; + } + } + + cut[i] = mincut; + } + + cut[length - 1] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic_cases() { + // "aab" -> "aa" | "b" = 1 cut + assert_eq!(minimum_palindrome_partitions("aab"), 1); + + // "aaa" is already a palindrome = 0 cuts + assert_eq!(minimum_palindrome_partitions("aaa"), 0); + + // Complex case + assert_eq!(minimum_palindrome_partitions("ababbbabbababa"), 3); + } + + #[test] + fn test_edge_cases() { + // Empty string + assert_eq!(minimum_palindrome_partitions(""), 0); + + // Single character is always a palindrome + assert_eq!(minimum_palindrome_partitions("a"), 0); + + // Two different characters need 1 cut + assert_eq!(minimum_palindrome_partitions("ab"), 1); + } + + #[test] + fn test_palindromes() { + // Already a palindrome + assert_eq!(minimum_palindrome_partitions("racecar"), 0); + assert_eq!(minimum_palindrome_partitions("noon"), 0); + assert_eq!(minimum_palindrome_partitions("abba"), 0); + } + + #[test] + fn test_non_palindromes() { + // All different characters need n-1 cuts + assert_eq!(minimum_palindrome_partitions("abcde"), 4); + + // Two pairs need 1 cut + assert_eq!(minimum_palindrome_partitions("aabb"), 1); + } + + #[test] + fn test_longer_strings() { + assert_eq!(minimum_palindrome_partitions("aaabaa"), 1); + assert_eq!(minimum_palindrome_partitions("abcbm"), 2); + } +}