From 90766e45dcc0cb9e490644671eba86dddc1d7a5c Mon Sep 17 00:00:00 2001 From: Luca Lombardo Date: Wed, 8 Nov 2023 10:55:53 +0100 Subject: [PATCH] longest common subsequence --- .../longest-common-subsequence/Cargo.toml | 8 +++++ .../longest-common-subsequence/README.md | 17 ++++++++++ .../longest-common-subsequence/src/main.rs | 31 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 2023_11_09/longest-common-subsequence/Cargo.toml create mode 100644 2023_11_09/longest-common-subsequence/README.md create mode 100644 2023_11_09/longest-common-subsequence/src/main.rs diff --git a/2023_11_09/longest-common-subsequence/Cargo.toml b/2023_11_09/longest-common-subsequence/Cargo.toml new file mode 100644 index 0000000..728afb7 --- /dev/null +++ b/2023_11_09/longest-common-subsequence/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "longest-common-subsequence" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2023_11_09/longest-common-subsequence/README.md b/2023_11_09/longest-common-subsequence/README.md new file mode 100644 index 0000000..20adb23 --- /dev/null +++ b/2023_11_09/longest-common-subsequence/README.md @@ -0,0 +1,17 @@ +## Technique + +The technique used here is a bottom-up dynamic programming approach. The main steps are as follows: + +1. **Initialization**: A 2D DP table `dp` is created with dimensions `(text1.len() + 1) x (text2.len() + 1)`. The extra row and column are for the base case when one of the strings is empty. + +2. **Conversion to Character Vectors**: The strings `text1` and `text2` are converted to vectors of characters. This is done because strings in Rust are not indexable due to their UTF-8 encoding. Converting them to character vectors allows for easier indexing. + +3. **Filling the DP Table**: The outer loop iterates over `text1` in reverse order, and the inner loop iterates over `text2` in reverse order. For each pair of characters `text1[i]` and `text2[j]`, it checks if they are equal. If they are equal, it increments the length of the current longest common subsequence, which is stored in `dp[i+1][j+1]`, by 1 and stores it in `dp[i][j]`. If they are not equal, it takes the maximum length of the common subsequence found so far, which is the maximum of `dp[i][j+1]` and `dp[i+1][j]`, and stores it in `dp[i][j]`. + +4. **Result**: After all iterations, `dp[0][0]` will have the length of the longest common subsequence of `text1` and `text2`. + +## Time and Space Complexity Analysis + +**Time Complexity**: The time complexity of this approach is O(m*n), where m and n are the lengths of `text1` and `text2`, respectively. This is because each cell in the DP table is filled exactly once. + +**Space Complexity**: The space complexity is also O(m*n) due to the 2D DP table. Each cell in the table stores an integer, and there are (m+1)*(n+1) cells in total. diff --git a/2023_11_09/longest-common-subsequence/src/main.rs b/2023_11_09/longest-common-subsequence/src/main.rs new file mode 100644 index 0000000..40ba403 --- /dev/null +++ b/2023_11_09/longest-common-subsequence/src/main.rs @@ -0,0 +1,31 @@ +impl Solution { + pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + // bottom-up dynamic programming approach + // we just created a 2D grid of length len(text1)+1 x len(text2) + 1 and initializing it to all zeros + let mut dp = vec![vec![0; text2.len() + 1]; text1.len() + 1]; + + // converting the strings to character vectors is to allow for easier indexing. In Rust, strings are not indexable due to their encoding in UTF-8, which means a single character can span more than one byte. When you try to index a string directly, you might end up indexing in the middle of a character, which would result in an error. By converting the string to a vector of characters, you ensure that each index corresponds to a complete character, avoiding this issue. + let text1 = text1.chars().collect::>(); + let text2 = text2.chars().collect::>(); + + for i in (0..text1.len()).rev() { + for j in (0..text2.len()).rev() { + if text1[i] == text2[j] { + dp[i][j] = 1 + dp[i+1][j+1]; + } else { + dp[i][j] = std::cmp::max(dp[i][j+1], dp[i+1][j]); + } + } + } + + dp[0][0] + } +} + +struct Solution; + +fn main() { + assert_eq!(Solution::longest_common_subsequence("abcde".to_string(), "ace".to_string()), 3); + assert_eq!(Solution::longest_common_subsequence("abc".to_string(), "abc".to_string()), 3); + assert_eq!(Solution::longest_common_subsequence("abc".to_string(), "def".to_string()), 0); +}