Longest Common Subsequence
MEDIUMDescription
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.
Example 1:
Input: text1 = "abcde", text2 = "ace" Output: 3 Explanation: The longest common subsequence is "ace" and its length is 3.
Example 2:
Input: text1 = "abc", text2 = "abc" Output: 3 Explanation: The longest common subsequence is "abc" and its length is 3.
Example 3:
Input: text1 = "abc", text2 = "def" Output: 0 Explanation: There is no such common subsequence, so the result is 0.
Constraints:
1 <= text1.length, text2.length <= 1000text1andtext2consist of only lowercase English characters.
Approaches
Checkout 4 different approaches to solve Longest Common Subsequence. Click on different approaches to view the approach and algorithm in detail.
Recursion with Memoization (Top-Down DP)
This approach optimizes the brute-force recursion by using a memoization table (usually a 2D array) to store the results of already computed subproblems. By caching results, we avoid redundant calculations for the same pair of indices (i, j). This technique is a form of top-down dynamic programming.
Algorithm
- Use the same recursive structure as the brute-force approach.
- Create a 2D array
memo[m][n]to store the results of subproblems, initialized with a value like -1. - In the recursive function
solve(i, j), first check ifmemo[i][j]is already computed (i.e., not -1). If so, return the stored value. - If not computed, calculate the result using the same recursive logic.
- Before returning the result, store it in
memo[i][j]for future use.
The recursive structure is the same as the brute-force approach, but we enhance it with a cache to store intermediate results.
- We introduce a 2D array,
memo, of sizem x n(wheremistext1.length()andnistext2.length()) to store the results ofsolve(i, j). - We initialize the
memotable with a special value (e.g., -1) to indicate that a subproblem has not been solved yet. - Inside the recursive function
solve(i, j), before any computation, we first check ifmemo[i][j]has already been computed. If it has, we return the stored value immediately, avoiding a costly re-computation. - If the result is not in the memo table, we compute it using the same recursive logic as before.
- After computing the result, we store it in
memo[i][j]before returning it.
By storing results, we ensure that each subproblem (i, j) is solved only once.
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
int[][] memo = new int[m][n];
for (int[] row : memo) {
java.util.Arrays.fill(row, -1);
}
return solve(text1, text2, 0, 0, memo);
}
private int solve(String text1, String text2, int i, int j, int[][] memo) {
if (i == text1.length() || j == text2.length()) {
return 0;
}
if (memo[i][j] != -1) {
return memo[i][j];
}
int result;
if (text1.charAt(i) == text2.charAt(j)) {
result = 1 + solve(text1, text2, i + 1, j + 1, memo);
} else {
int option1 = solve(text1, text2, i + 1, j, memo);
int option2 = solve(text1, text2, i, j + 1, memo);
result = Math.max(option1, option2);
}
memo[i][j] = result;
return result;
}
}
Complexity Analysis
Pros and Cons
- Drastically more efficient than brute-force, with polynomial time complexity.
- Retains the intuitive top-down recursive structure, making it easy to reason about.
- Requires O(m*n) space for the memoization table, which can be significant.
- For very deep recursion paths (not an issue with problem constraints), it could theoretically cause a stack overflow.
Code Solutions
Checking out 5 solutions in different languages for Longest Common Subsequence. Click on different languages to view the code.
public class Solution {
public int LongestCommonSubsequence(string text1, string text2) {
int m = text1.Length, n = text2.Length;
int[, ] f = new int[m + 1, n + 1];
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (text1[i - 1] == text2[j - 1]) {
f[i, j] = f[i - 1, j - 1] + 1;
} else {
f[i, j] = Math.Max(f[i - 1, j], f[i, j - 1]);
}
}
}
return f[m, n];
}
}Video Solution
Watch the video walkthrough for Longest Common Subsequence
Similar Questions
5 related questions you might find useful
Patterns:
Data Structures:
Companies:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.