diff --git a/Cargo.lock b/Cargo.lock index 4f75e37..9e49a6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,10 @@ name = "binary_tree_postorder_traversal" version = "0.1.0" +[[package]] +name = "edit_distance" +version = "0.1.0" + [[package]] name = "implement_queue_using_stacks" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9eb1a88..f5961ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ members = [ "intersection_of_two_arrays", "longest_palindromic_substring", "reverse_words_in_a_string_iii", + "edit_distance", ] diff --git a/README.md b/README.md index d0e4792..ce9b0c4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Resolving problems of LeetCode in RustLang. * [5. Longest Palindromic Substring](./longest_palindromic_substring/lib.rs) * [7. Reverse Integer](./reverse_integer/src/lib.rs) * [15. 3 Sum](./three_sum/src/lib.rs) +* [72. Edit Distance](./edit_distance/src/lib.rs) * [88. Merge Sorted Array](./merge_sorted_array/src/lib.rs) * [145. Binary Tree Postorder Traversal](./binary_tree_postorder_traversal/src/lib.rs) * [215. Kth Largest Element in an Array](./kth_largest/src/lib.rs) diff --git a/edit_distance/Cargo.toml b/edit_distance/Cargo.toml new file mode 100644 index 0000000..dec118e --- /dev/null +++ b/edit_distance/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "edit_distance" +version = "0.1.0" +authors = ["Ryan Li "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/edit_distance/src/lib.rs b/edit_distance/src/lib.rs new file mode 100644 index 0000000..7dcee63 --- /dev/null +++ b/edit_distance/src/lib.rs @@ -0,0 +1,77 @@ +/*! + * # 72. Edit Distance + * + * [Problem link](https://leetcode.com/problems/edit-distance/) + */ + +#![allow(dead_code)] + +struct Solution {} + +// ---------------------------------------------------------------------------- + +use std::collections::HashMap; + +impl Solution { + pub fn reduce_min_distance( + word1: &str, + word2: &str, + cache: &mut HashMap<(String, String), usize>, + ) -> usize { + if let Some(&min) = cache.get(&(word1.to_string(), word2.to_string())) { + return min; + }; + let (length1, length2) = (word1.len(), word2.len()); + if length1 == 0 || length2 == 0 { + let min = length1.max(length2); + cache.insert((word1.to_string(), word2.to_string()), min); + return min; + } + let word1_slice = word1[1..].to_string(); + let word2_slice = word2[1..].to_string(); + if word1.chars().next().unwrap() == word2.chars().next().unwrap() { + return Solution::reduce_min_distance(&word1_slice, &word2_slice, cache); + } + let insert = 1 + Solution::reduce_min_distance(word1, &word2_slice, cache); // insert the first char of word2 in front of word1 + let delete = 1 + Solution::reduce_min_distance(&word1_slice, word2, cache); // delete the first char of word1 + let edit = 1 + Solution::reduce_min_distance(&word1_slice, &word2_slice, cache); // change from the first char of word1 to the one of word2 + let min = *[insert, delete, edit].iter().min().unwrap(); + cache.insert((word1.to_string(), word2.to_string()), min); + min + } + + pub fn min_distance(word1: String, word2: String) -> i32 { + let mut cache = HashMap::new(); + Self::reduce_min_distance(&word1, &word2, &mut cache) as i32 + } +} + +// ---------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + + fn accept(word1: &str, word2: &str, expected: i32) { + assert_eq!( + Solution::min_distance(word1.to_string(), word2.to_string()), + expected + ); + } + + #[test] + fn test_example_1() { + let word1 = "horse"; + let word2 = "ros"; + let expected = 3; + accept(word1, word2, expected); + } + + #[test] + fn test_example_2() { + let word1 = "intention"; + let word2 = "execution"; + let expected = 5; + accept(word1, word2, expected); + } +}