Skip to content

Commit 5c4593c

Browse files
feat: add Palindrome Partitioning algorithm (#971)
1 parent 98400e1 commit 5c4593c

File tree

3 files changed

+137
-4
lines changed

3 files changed

+137
-4
lines changed

DIRECTORY.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
* [Maximum Subarray](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/maximum_subarray.rs)
111111
* [Minimum Cost Path](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/minimum_cost_path.rs)
112112
* [Optimal BST](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/optimal_bst.rs)
113+
* [Palindrome Partitioning](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/palindrome_partitioning.rs)
113114
* [Rod Cutting](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/rod_cutting.rs)
114115
* [Smith-Waterman](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/smith_waterman.rs)
115116
* [Snail](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/snail.rs)
@@ -150,7 +151,7 @@
150151
* [Segment](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/segment.rs)
151152
* Graph
152153
* [Astar](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/astar.rs)
153-
* [Bellman Ford](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bellman_ford.rs)
154+
* [Bellman-Ford](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bellman_ford.rs)
154155
* [Bipartite Matching](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/bipartite_matching.rs)
155156
* [Breadth First Search](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/breadth_first_search.rs)
156157
* [Centroid Decomposition](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/centroid_decomposition.rs)
@@ -233,12 +234,12 @@
233234
* [Geometric Series](https://github.com/TheAlgorithms/Rust/blob/master/src/math/geometric_series.rs)
234235
* [Greatest Common Divisor](https://github.com/TheAlgorithms/Rust/blob/master/src/math/greatest_common_divisor.rs)
235236
* [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/math/huber_loss.rs)
236-
* [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/math/infix_to_postfix.rs)
237+
* [Infix to Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/math/infix_to_postfix.rs)
237238
* [Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interest.rs)
238239
* [Interpolation](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interpolation.rs)
239240
* [Interquartile Range](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interquartile_range.rs)
240241
* [Karatsuba Multiplication](https://github.com/TheAlgorithms/Rust/blob/master/src/math/karatsuba_multiplication.rs)
241-
* [LCM Of N Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/lcm_of_n_numbers.rs)
242+
* [LCM of N Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/lcm_of_n_numbers.rs)
242243
* [Leaky Relu](https://github.com/TheAlgorithms/Rust/blob/master/src/math/leaky_relu.rs)
243244
* [Least Square Approx](https://github.com/TheAlgorithms/Rust/blob/master/src/math/least_square_approx.rs)
244245
* [Linear Sieve](https://github.com/TheAlgorithms/Rust/blob/master/src/math/linear_sieve.rs)
@@ -249,7 +250,7 @@
249250
* [Miller Rabin](https://github.com/TheAlgorithms/Rust/blob/master/src/math/miller_rabin.rs)
250251
* [Modular Exponential](https://github.com/TheAlgorithms/Rust/blob/master/src/math/modular_exponential.rs)
251252
* [Newton Raphson](https://github.com/TheAlgorithms/Rust/blob/master/src/math/newton_raphson.rs)
252-
* [Nthprime](https://github.com/TheAlgorithms/Rust/blob/master/src/math/nthprime.rs)
253+
* [N-th prime](https://github.com/TheAlgorithms/Rust/blob/master/src/math/nthprime.rs)
253254
* [Pascal Triangle](https://github.com/TheAlgorithms/Rust/blob/master/src/math/pascal_triangle.rs)
254255
* [Perfect Cube](https://github.com/TheAlgorithms/Rust/blob/master/src/math/perfect_cube.rs)
255256
* [Perfect Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/perfect_numbers.rs)

src/dynamic_programming/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod maximal_square;
1414
mod maximum_subarray;
1515
mod minimum_cost_path;
1616
mod optimal_bst;
17+
mod palindrome_partitioning;
1718
mod rod_cutting;
1819
mod smith_waterman;
1920
mod snail;
@@ -47,6 +48,7 @@ pub use self::maximal_square::maximal_square;
4748
pub use self::maximum_subarray::maximum_subarray;
4849
pub use self::minimum_cost_path::minimum_cost_path;
4950
pub use self::optimal_bst::optimal_search_tree;
51+
pub use self::palindrome_partitioning::minimum_palindrome_partitions;
5052
pub use self::rod_cutting::rod_cut;
5153
pub use self::smith_waterman::{score_function, smith_waterman, traceback};
5254
pub use self::snail::snail;
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/// Finds the minimum cuts needed for a palindrome partitioning of a string
2+
///
3+
/// Given a string s, partition s such that every substring of the partition is a palindrome.
4+
/// This function returns the minimum number of cuts needed.
5+
///
6+
/// Time Complexity: O(n^2)
7+
/// Space Complexity: O(n^2)
8+
///
9+
/// # Arguments
10+
///
11+
/// * `s` - The input string to partition
12+
///
13+
/// # Returns
14+
///
15+
/// The minimum number of cuts needed
16+
///
17+
/// # Examples
18+
///
19+
/// ```
20+
/// use the_algorithms_rust::dynamic_programming::minimum_palindrome_partitions;
21+
///
22+
/// assert_eq!(minimum_palindrome_partitions("aab"), 1);
23+
/// assert_eq!(minimum_palindrome_partitions("aaa"), 0);
24+
/// assert_eq!(minimum_palindrome_partitions("ababbbabbababa"), 3);
25+
/// ```
26+
///
27+
/// # Algorithm Explanation
28+
///
29+
/// The algorithm uses dynamic programming with two key data structures:
30+
/// - `cut[i]`: minimum cuts needed for substring from index 0 to i
31+
/// - `is_palindromic[j][i]`: whether substring from index j to i is a palindrome
32+
///
33+
/// For each position i, we check all possible starting positions j to determine
34+
/// if the substring s[j..=i] is a palindrome. If it is, we update the minimum
35+
/// cut count accordingly.
36+
///
37+
/// Reference: <https://www.youtube.com/watch?v=_H8V5hJUGd0>
38+
pub fn minimum_palindrome_partitions(s: &str) -> usize {
39+
let chars: Vec<char> = s.chars().collect();
40+
let length = chars.len();
41+
42+
if length == 0 {
43+
return 0;
44+
}
45+
46+
// cut[i] represents the minimum cuts needed for substring from 0 to i
47+
let mut cut = vec![0; length];
48+
49+
// is_palindromic[j][i] represents whether substring from j to i is a palindrome
50+
let mut is_palindromic = vec![vec![false; length]; length];
51+
52+
for i in 0..length {
53+
let mut mincut = i;
54+
55+
for j in 0..=i {
56+
// Check if substring from j to i is a palindrome
57+
// A substring is a palindrome if:
58+
// 1. The characters at both ends match (chars[i] == chars[j])
59+
// 2. AND either:
60+
// - The substring length is less than 2 (single char or two same chars)
61+
// - OR the inner substring (j+1 to i-1) is also a palindrome
62+
if chars[i] == chars[j] && (i - j < 2 || is_palindromic[j + 1][i - 1]) {
63+
is_palindromic[j][i] = true;
64+
mincut = if j == 0 {
65+
// If the entire substring from 0 to i is a palindrome, no cuts needed
66+
0
67+
} else {
68+
// Otherwise, take minimum of current mincut and (cuts up to j-1) + 1
69+
mincut.min(cut[j - 1] + 1)
70+
};
71+
}
72+
}
73+
74+
cut[i] = mincut;
75+
}
76+
77+
cut[length - 1]
78+
}
79+
80+
#[cfg(test)]
81+
mod tests {
82+
use super::*;
83+
84+
#[test]
85+
fn test_basic_cases() {
86+
// "aab" -> "aa" | "b" = 1 cut
87+
assert_eq!(minimum_palindrome_partitions("aab"), 1);
88+
89+
// "aaa" is already a palindrome = 0 cuts
90+
assert_eq!(minimum_palindrome_partitions("aaa"), 0);
91+
92+
// Complex case
93+
assert_eq!(minimum_palindrome_partitions("ababbbabbababa"), 3);
94+
}
95+
96+
#[test]
97+
fn test_edge_cases() {
98+
// Empty string
99+
assert_eq!(minimum_palindrome_partitions(""), 0);
100+
101+
// Single character is always a palindrome
102+
assert_eq!(minimum_palindrome_partitions("a"), 0);
103+
104+
// Two different characters need 1 cut
105+
assert_eq!(minimum_palindrome_partitions("ab"), 1);
106+
}
107+
108+
#[test]
109+
fn test_palindromes() {
110+
// Already a palindrome
111+
assert_eq!(minimum_palindrome_partitions("racecar"), 0);
112+
assert_eq!(minimum_palindrome_partitions("noon"), 0);
113+
assert_eq!(minimum_palindrome_partitions("abba"), 0);
114+
}
115+
116+
#[test]
117+
fn test_non_palindromes() {
118+
// All different characters need n-1 cuts
119+
assert_eq!(minimum_palindrome_partitions("abcde"), 4);
120+
121+
// Two pairs need 1 cut
122+
assert_eq!(minimum_palindrome_partitions("aabb"), 1);
123+
}
124+
125+
#[test]
126+
fn test_longer_strings() {
127+
assert_eq!(minimum_palindrome_partitions("aaabaa"), 1);
128+
assert_eq!(minimum_palindrome_partitions("abcbm"), 2);
129+
}
130+
}

0 commit comments

Comments
 (0)