Back to blog
Nov 21, 2024
3 min read

LeetCode 1143: Longest Common Subsequence

A detailed solution to the classic Longest Common Subsequence (LCS) problem using dynamic programming

Problem Description

LeetCode Problem 1143

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.

A common subsequence of two strings is a subsequence that is common to both strings.

Examples

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 common subsequence between the two strings.

Constraints

  • 1 <= text1.length, text2.length <= 1000
  • text1 and text2 consist of only lowercase English characters.

Dynamic Programming Solution

We can solve this using a 2D DP table where:

  • Rows represent characters from text1
  • Columns represent characters from text2
  • Each cell [i][j] represents the length of LCS for text1[0…i-1] and text2[0…j-1]

The DP state transition:

  1. If characters match: dp[i][j] = 1 + dp[i-1][j-1]
  2. If characters don’t match: dp[i][j] = max(dp[i-1][j], dp[i][j-1])

Implementation

Here’s the Python implementation:

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        n, m = len(text1), len(text2)
        grid = [[0] * (m+1) for _ in range(n+1)]
        
        for r in range(1, n+1):
            for c in range(1, m+1):
                if text1[r-1] == text2[c-1]:
                    grid[r][c] = 1 + grid[r-1][c-1]
                else:
                    grid[r][c] = max(grid[r-1][c], grid[r][c-1])
        
        return grid[n][m]

Bonus: Retrieving the Subsequence

While the problem only asks for the length, we can also retrieve the actual subsequence:

def getLCS(self, text1: str, text2: str) -> str:
    n, m = len(text1), len(text2)
    grid = [[0] * (m+1) for _ in range(n+1)]
    
    # Fill the grid as before
    for r in range(1, n+1):
        for c in range(1, m+1):
            if text1[r-1] == text2[c-1]:
                grid[r][c] = 1 + grid[r-1][c-1]
            else:
                grid[r][c] = max(grid[r-1][c], grid[r][c-1])
    
    # Backtrack to find the subsequence
    subsequence = ""
    r, c = n, m
    while r > 0 and c > 0:
        if text1[r-1] == text2[c-1]:
            subsequence = text1[r-1] + subsequence
            r -= 1
            c -= 1
        elif grid[r-1][c] > grid[r][c-1]:
            r -= 1
        else:
            c -= 1
    
    return subsequence

Complexity Analysis

  • Time Complexity: O(n×m)O(n × m), where n and m are the lengths of the input strings

    • We need to fill every cell in the DP table exactly once
    • Each cell operation is O(1)
  • Space Complexity: O(n×m)O(n × m)

    • We need a 2D table to store the intermediate results
    • This can be optimized to O(min(n,m)) by using only two rows