Problem Statement

Longest Common Subsequence

You get two strings, text1 and text2. You want the length of their longest common subsequence. A subsequence is what you get when you start with a string and delete some letters (maybe none), but you keep the letters you keep in their original order. For example, "ace" is a subsequence of "abcde" because you can cross out the b and the d and the rest still reads "ace" in order. A common subsequence is a subsequence that shows up in both strings. We want the longest one and we return its length. If they share nothing, the answer is 0. The tool for this is a 2D DP table. DP means dynamic programming, which just means we solve tiny pieces of the problem first, write the answers in a grid, and reuse those answers to build up to the big answer. A 2D table is a grid with rows and columns, like a spreadsheet, where each cell holds the answer to one small piece. This fits because the answer for two strings depends on the answers for their shorter pieces, and a grid is the natural place to store those.

mediumDynamic ProgrammingStringDynamic ProgrammingTime: O(m * n) · Space: O(m * n)

Signals to notice

longest common subsequence of two stringsnot contiguouscharacters in order

Brute force first

Generate all subsequences of both strings and find the longest common one. Exponential. That instinct is useful because it follows the prompt literally, but it usually keeps revisiting work the problem is begging you to organize.

The key insight

2D DP: dp[i][j] = LCS length of s1[0.i-1] and s2[0.j-1]. If s1[i-1] == s2[j-1], dp[i][j] = dp[i-1][j-1] + 1. Otherwise, dp[i][j] = max(dp[i-1][j], dp[i][j-1]). The goal is not to be clever for its own sake, but to remember the one relationship that keeps the solution grounded as you move forward.

Trace it on text1="abcde", text2="ace"

init: dp is a 6x4 grid of 0s (m+1=6 rows, n+1=4 cols; row/col 0 are the empty-string base cases)
i=1 'a': matches 'a' (j=1) -> dp[1][1]=dp[0][0]+1=1; rest of row stays 1
i=2 'b': no match in "ace" -> row copies max from above/left, stays 1
i=3 'c': matches 'c' (j=2) -> dp[3][2]=dp[2][1]+1=2; dp[3][3]=max(dp[2][3],dp[3][2])=2
i=4 'd': no match -> dp[4][3]=max(dp[3][3],dp[4][2])=2
i=5 'e': matches 'e' (j=3) -> dp[5][3]=dp[4][2]+1=2+1=3
return dp[5][3] = 3  (LCS "ace")

What must stay true

When characters match, both are part of the LCS — extend from dp[i-1][j-1]. When they don't, the LCS either skips s1[i-1] or s2[j-1] — take the longer option. As long as that statement keeps holding, you can trust the steps built on top of it.

Shape of the loop

dp = grid of zeros, size (m+1) x (n+1)
for i in 1..m:
  for j in 1..n:
    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[m][n]

Pseudocode only — the full worked solution lives in the Solution tab.

Easy way to go wrong

Confusing longest common SUBSEQUENCE with longest common SUBSTRING — subsequences don't need to be contiguous. When characters don't match, you take max, not reset to 0. Most mistakes here are not about syntax; they come from losing track of what your state, pointer, or structure is supposed to mean.

Dynamic Programming Pattern