Description
Given two strings text1
and text2
, return the length of their longest common subsequence. If there is no common subsequence, return 0
.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
- For example, “ace” is a subsequence of “abcde”.
A common subsequence of two strings is a subsequence that is common to both strings.
Solution
If we are finding the longest common subsequence of two strings, "abcde"
and "ace"
and starting from the first character:
- (a)
'a' == 'a'
, therefore, our problem becomes a sub problem of finding the longest common subsequence of"bcde"
and"ce"
. - (b) Then we compare
'b'
and'c'
, which are not equal. Therefore, the longest common subsequence might belongestCommonSubsequence("bcde", "e")
orlongestCommonSubsequence("cde", "ce")
. We will be using the longer one.
By the above observation we could solve this problem using dynamic programming with a 2D DP table where:
- x and y-axis are the two string’s characters
- If
str[i] == str[j]
, thendp[i][j]
will bedp[i-1][j-1] + 1
. (a) - If
str[i] != str[j]
, thendp[i][j]
will be the maximum ofdp[i][j-1]
anddp[i-1][j]
. (longest common subsequence ofstr1[0:i], str2[0:j-1]
andstr1[0:i-1], str2[0:j]
. (b)
Implementation
Did a little trick to remove the boundary checks in this implementation. The DP table’s dimention is one larger than the inputs’ sizes. This way, the i=0
row and j=0
column are the initial state 0. See the table below:
a | b | c | d | e | ||
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | |
a | 0 | 1 | 1 | 1 | 1 | 1 |
c | 0 | 1 | 1 | 2 | 2 | 2 |
e | 0 | 1 | 1 | 2 | 2 | 3 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int longestCommonSubsequence(const string& text1, const string& text2) {
size_t m = text1.size() + 1;
size_t n = text2.size() + 1;
vector<vector<int>> dp(m, vector<int>(n));
for (size_t i = 1; i < m; i++) {
for (size_t j = 1; j < n; j++) {
if (text1[i-1] == text2[j-1])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
return dp.back().back();
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
impl Solution {
pub fn longest_common_subsequence(text1: String, text2: String) -> i32 {
let (m, n) = (text1.len()+1, text2.len()+1);
let mut dp: Vec<Vec<i32>> = vec![vec![0; n]; m];
for (i, c1) in text1.chars().enumerate() {
for (j, c2) in text2.chars().enumerate() {
dp[i+1][j+1] = if c1 == c2 {
dp[i][j] + 1
} else {
std::cmp::max(dp[i][j+1], dp[i+1][j])
}
}
}
dp[m-1][n-1]
}
}