Longest Common Subsequence
Expert Answer & Key Takeaways
A complete guide to understanding and implementing Dynamic Programming.
Longest Common Subsequence
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.
Visual Representation
text1 = "abcde", text2 = "ace"
LCS is "ace", Length = 3
DP Grid Logic:
- If text1[i] == text2[j]:
1 + dp[i+1][j+1] (Match!)
- Else:
max(dp[i+1][j], dp[i][j+1]) (Skip one char)Examples
Input: text1 = "abcde", text2 = "ace"
Output: 3
The longest common subsequence is "ace" and its length is 3.
Input: text1 = "abc", text2 = "abc"
Output: 3
Approach 1
Level I: Brute Force (Recursive)
Intuition
We compare characters of both strings starting from the beginning. If they match, we increment the count and move both pointers. If they don't match, we have two choices: skip a character from
text1 or skip a character from text2. We take the maximum of these two paths.Thought Process
solve(i, j): LCS oftext1[i:]andtext2[j:].- If
text1[i] == text2[j]:1 + solve(i+1, j+1). - Else:
max(solve(i+1, j), solve(i, j+1)).
Pattern Identification
This is Recursive Decision Tree. The time complexity is exponential because it explores the same (i, j) pairs multiple times.
⏱ O(2^(n+m))💾 O(n+m) for recursion stack
Detailed Dry Run
text1="abc", text2="ace"
| i | j | Match? | Action | Returns |
|---|---|---|---|---|
| 0 | 0 | 'a'=='a' (Yes) | 1 + f(1, 1) | 1 + 2 = 3 |
| 1 | 1 | 'b'=='c' (No) | max(f(2, 1), f(1, 2)) | max(1, 2) = 2 |
| 1 | 2 | 'b'=='e' (No) | max(f(2, 2), f(1, 3)) | max(1, 0) = 1 |
Approach 2
Level II: Memoization (Top-Down 2D)
Intuition
The recursive LCS solution re-evaluates the same
(i, j) pairs many times. We add a 2D memo table indexed by string positions.Visual
text1="abc", text2="ace"
Memo table after caching:
"" 'a' 'c' 'e'
"" 0 0 0 0
'a' 0 [3] - - <- solve(0,0)=3 cached!
'b' 0 - - -
'c' 0 - [1] -⏱ O(N * M)💾 O(N * M)
Detailed Dry Run
solve(0, 0): 'a'=='a', return 1+solve(1,1). solve(1,1): 'b'!='c', return max(solve(2,1), solve(1,2)).
⚠️ Common Pitfalls & Tips
Python's
lru_cache automatically creates the 2D memo table. In Java, a 2D array initialized with -1 suffices for string indices.Approach 3
Level III: Optimal (2D DP)
Intuition
Thought Process
We use a 2D grid where
dp[i][j] represents the LCS length for text1[i:] and text2[j:]. We fill the grid from the bottom-right towards the top-left (or top-left to bottom-right).Logic Steps
- Initialize a 2D array
dpof size(m+1) x (n+1)with 0s. - Iterate through
i(text1) andj(text2) in reverse. - If characters match:
dp[i][j] = 1 + dp[i+1][j+1]. - Else:
dp[i][j] = max(dp[i+1][j], dp[i][j+1]). - Return
dp[0][0].
⏱ O(N * M) where N, M are lengths of the strings💾 O(N * M)
Detailed Dry Run
text1="abc", text2="ace"
| Row (i) | Col (j) | Match? | dp[i][j] calculation | Value |
|---|---|---|---|---|
| 2 (c) | 2 (e) | No | max(dp[3][2], dp[2][3]) | 0 |
| 2 (c) | 1 (c) | Yes | 1 + dp[3][2] | 1 |
| 1 (b) | 1 (c) | No | max(dp[2][1], dp[1][2]) | 1 |
| 0 (a) | 0 (a) | Yes | 1 + dp[1][1] | 3 |
⚠️ Common Pitfalls & Tips
Remember to initialize the
dp table with size (n+1) x (m+1) to handle the base cases (empty strings) easily without extra boundary checks.Course4All Technical Board
Verified ExpertSenior Software Engineers & Algorithmic Experts
Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.
Pattern: 2026 Ready
Updated: Weekly
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.