โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
โข Option
โข Result
โข The ? operator propagates errors up the call stack concisely
โข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
โข The compiler forces you to handle every error case โ no silent failures
// 1065: Combination Sum โ Find Combos Summing to Target
// Approach 1: Backtracking with reuse allowed
fn combination_sum(candidates: &mut Vec<i32>, target: i32) -> Vec<Vec<i32>> {
candidates.sort();
let mut results = Vec::new();
let mut current = Vec::new();
fn backtrack(
start: usize, remaining: i32,
candidates: &[i32], current: &mut Vec<i32>, results: &mut Vec<Vec<i32>>,
) {
if remaining == 0 {
results.push(current.clone());
return;
}
for i in start..candidates.len() {
if candidates[i] > remaining { break; } // sorted, so prune
current.push(candidates[i]);
backtrack(i, remaining - candidates[i], candidates, current, results);
current.pop();
}
}
backtrack(0, target, candidates, &mut current, &mut results);
results
}
// Approach 2: Functional with iterators
fn combination_sum_func(candidates: &[i32], target: i32) -> Vec<Vec<i32>> {
let mut sorted = candidates.to_vec();
sorted.sort();
fn solve(start: usize, remaining: i32, sorted: &[i32]) -> Vec<Vec<i32>> {
if remaining == 0 { return vec![vec![]]; }
if remaining < 0 { return vec![]; }
let mut results = Vec::new();
for i in start..sorted.len() {
if sorted[i] > remaining { break; }
for mut combo in solve(i, remaining - sorted[i], sorted) {
combo.insert(0, sorted[i]);
results.push(combo);
}
}
results
}
solve(0, target, &sorted)
}
// Approach 3: Combination sum II (each number used once)
fn combination_sum_unique(candidates: &mut Vec<i32>, target: i32) -> Vec<Vec<i32>> {
candidates.sort();
let mut results = Vec::new();
let mut current = Vec::new();
fn backtrack(
start: usize, remaining: i32,
candidates: &[i32], current: &mut Vec<i32>, results: &mut Vec<Vec<i32>>,
) {
if remaining == 0 {
results.push(current.clone());
return;
}
for i in start..candidates.len() {
if candidates[i] > remaining { break; }
if i > start && candidates[i] == candidates[i - 1] { continue; } // skip dupes
current.push(candidates[i]);
backtrack(i + 1, remaining - candidates[i], candidates, current, results);
current.pop();
}
}
backtrack(0, target, candidates, &mut current, &mut results);
results
}
fn main() {
let mut cands = vec![2, 3, 6, 7];
let results = combination_sum(&mut cands, 7);
println!("Combinations summing to 7: {:?}", results);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_combination_sum() {
let mut cands = vec![2, 3, 6, 7];
let r = combination_sum(&mut cands, 7);
assert_eq!(r.len(), 2);
assert!(r.contains(&vec![2, 2, 3]));
assert!(r.contains(&vec![7]));
}
#[test]
fn test_combination_sum_func() {
let r = combination_sum_func(&[2, 3, 5], 8);
assert_eq!(r.len(), 3);
}
#[test]
fn test_combination_sum_unique() {
let mut cands = vec![10, 1, 2, 7, 6, 1, 5];
let r = combination_sum_unique(&mut cands, 8);
assert!(r.contains(&vec![1, 1, 6]));
assert!(r.contains(&vec![1, 2, 5]));
assert!(r.contains(&vec![1, 7]));
assert!(r.contains(&vec![2, 6]));
}
}
(* 1065: Combination Sum โ Find Combos Summing to Target *)
(* Approach 1: Backtracking with reuse allowed *)
let combination_sum candidates target =
let sorted = List.sort compare candidates in
let results = ref [] in
let rec backtrack start current remaining =
if remaining = 0 then
results := List.rev current :: !results
else if remaining > 0 then
List.iteri (fun idx c ->
if idx >= start && c <= remaining then
backtrack idx (c :: current) (remaining - c)
) sorted
in
backtrack 0 [] target;
List.rev !results
(* Approach 2: Functional with list accumulation *)
let combination_sum_func candidates target =
let sorted = List.sort compare candidates in
let rec solve start remaining =
if remaining = 0 then [[]]
else if remaining < 0 then []
else
List.filteri (fun i _ -> i >= start) sorted
|> List.concat_map (fun (c : int) ->
if c > remaining then []
else
let idx = List.filteri (fun i _ -> i >= start) sorted
|> List.mapi (fun i x -> (i + start, x))
|> List.find (fun (_, x) -> x = c)
|> fst in
List.map (fun rest -> c :: rest) (solve idx (remaining - c))
)
in
solve 0 target
(* Approach 3: Array-based for efficiency *)
let combination_sum_arr candidates target =
let arr = Array.of_list (List.sort compare candidates) in
let n = Array.length arr in
let results = ref [] in
let current = ref [] in
let rec backtrack start remaining =
if remaining = 0 then
results := List.rev !current :: !results
else
for i = start to n - 1 do
if arr.(i) <= remaining then begin
current := arr.(i) :: !current;
backtrack i (remaining - arr.(i));
current := List.tl !current
end
done
in
backtrack 0 target;
List.rev !results
let () =
let r1 = combination_sum [2; 3; 6; 7] 7 in
assert (List.length r1 = 2); (* [2;2;3] and [7] *)
let r2 = combination_sum [2; 3; 5] 8 in
assert (List.length r2 = 3); (* [2;2;2;2], [2;3;3], [3;5] *)
let r3 = combination_sum_arr [2; 3; 6; 7] 7 in
assert (List.length r3 = 2);
Printf.printf "โ All tests passed\n"
| Aspect | OCaml | Rust |
|---|---|---|
| Sorting | `List.sort compare` (returns new list) | `.sort()` (in-place) |
| Pruning | Index filtering with `List.iteri` | `break` in sorted loop |
| Result building | Prepend `::` + `List.rev` | `push` + `pop` |
| Reuse control | Recurse with same index | Recurse with same `i` |
| Dedup (variant) | Would need explicit skip | `if i > start && arr[i] == arr[i-1]` |