Longest Common Subsequence
Master this topic with zero to advance depth.
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
The longest common subsequence is "ace" and its length is 3.
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.
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 |
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] -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.
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].
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.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.