When a coding interview asks you to compare two strings, arrays, or any ordered collections, the underlying problem often reduces to finding the longest common subsequence (LCS). Unlike a substring, a subsequence does not need to be contiguous; it only has to preserve the original order. Recognizing this pattern early saves you from reinventing ad‑hoc solutions and lets you apply a well‑understood dynamic‑programming (DP) approach.

Why LCS Appears So Often

  • Edit distance / minimum operations – Transforming one string into another by inserting or deleting characters is equivalent to finding the LCS and counting the characters that are not part of it.
  • Preserving relative order – Problems that ask for the “maximum set of items that appear in the same order in both lists” are directly asking for an LCS.
  • Commonality across sequences – When the goal is to maximize shared content while allowing gaps, LCS is the natural model.

If you see any of these signals in the problem statement, stop and ask yourself, “What if I treat the inputs as sequences and look for the longest ordered overlap?” That question often leads straight to the LCS DP formulation.

The Classic DP Formulation

Given two sequences A of length m and B of length n, define dp[i][j] as the length of the LCS of the prefixes A[:i] and B[:j]. The recurrence is:

if A[i-1] == B[j-1]:
    dp[i][j] = dp[i-1][j-1] + 1
else:
    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

The base case is dp[0][*] = dp[*][0] = 0. The answer lives in dp[m][n].

Python Implementation (Space‑Optimized)

def lcs_length(a: str, b: str) -> int:
    """Return length of longest common subsequence of a and b.
    Uses O(min(len(a), len(b))) extra space.
    """
    # Ensure the shorter string drives the inner loop for memory savings
    if len(b) < len(a):
        a, b = b, a
    prev = [0] * (len(a) + 1)
    for ch_b in b:
        curr = [0]
        for i, ch_a in enumerate(a, 1):
            if ch_a == ch_b:
                curr.append(prev[i-1] + 1)
            else:
                curr.append(max(prev[i], curr[-1]))
        prev = curr
    return prev[-1]

The function runs in O(m·n) time and O(min(m,n)) space, which is typically fast enough for interview constraints (strings up to a few thousand characters).

Complexity at a Glance

MetricValue
TimeO(m·n) – each cell of the DP table is visited once
SpaceO(min(m,n)) with the rolling‑array trick; O(m·n) if you need the full table for reconstruction
Reconstructing the actual subsequenceO(m·n) time, O(m+n) additional space

If the input sizes are huge (e.g., >10⁵), you may need to look for specialized algorithms like the Hunt‑Szymanski method, but those rarely appear in a typical interview.

Common Pitfalls and How to Avoid Them

  1. Off‑by‑one indexing – Remember that dp[i][j] refers to the first i characters of A and the first j characters of B. Using zero‑based indices directly in the recurrence leads to index errors.
  2. Recomputing rows – A naïve recursive solution without memoization explodes exponentially. Always convert the recursion to an iterative DP or use functools.lru_cache if you prefer recursion.
  3. Confusing subsequence with substring – A substring must be contiguous; a subsequence can skip characters. If the problem explicitly mentions “contiguous” or “window”, you need a different approach (e.g., longest common substring).
  4. Forgetting to handle empty inputs – The base case of an empty string should return 0. A missing base case can cause a IndexError.
  5. Premature optimization – Trying to shave a constant factor before you have a correct DP often introduces bugs. Get the correct recurrence first, then optimize space if needed.

Five Representative Practice Problems

Below are five problems that each require the LCS pattern, but they vary the surrounding context to keep you honest.

1. Minimum Deletions to Make Two Strings Anagrams

Prompt: Given strings s1 and s2, delete characters from either string so that the remaining characters are anagrams of each other. Return the minimum number of deletions. Hint: The characters you keep must appear in the same order in both strings, i.e., they form a common subsequence. Compute the LCS length L; the answer is len(s1) + len(s2) - 2*L.

2. Aligning Two Event Timelines

Prompt: Two log files record timestamps of events (as increasing integers). You may drop events from either log. Find the maximum number of events that can be aligned in order. Hint: Treat each log as a sequence of integers. The longest common subsequence of the two sequences gives the maximal aligned set.

3. Word‑Level LCS for Sentence Similarity

Prompt: Given two sentences, split them into words. Return the length of the longest common subsequence of words. Hint: The DP works exactly as with characters; just replace the equality check with a word comparison.

4. Transform One String into Another with Insertions Only

Prompt: You can only insert characters into source to obtain target. What is the minimum number of insertions required? Hint: Characters that already appear in order need not be inserted. The LCS length tells you how many characters are already correctly placed. Answer = len(target) - LCS(source, target).

5. Two‑Robot Path Synchronization

Prompt: Two robots move on a grid, each producing a sequence of moves (U, D, L, R). You may discard moves from either robot. What is the longest sequence of moves they can perform together while preserving each robot's order? Hint: Model each robot's move list as a string and apply the standard LCS DP.

Working through these problems forces you to map the abstract DP to concrete story contexts, a skill interviewers value.

How to Practice This

  1. Write the DP from scratch – Start with the 2‑D table version, then refactor to the rolling‑array version. Verify both produce the same result on random test cases.
  2. Reconstruct the subsequence – After you have the length, backtrack through the DP table to output the actual LCS. This reinforces understanding of the recurrence.
  3. Explain the pattern aloud – Use Call Assistant to record yourself describing why a problem maps to LCS and walk through the solution. Listening back helps catch gaps in reasoning and keeps you focused on the pattern during real interviews.

FAQ

  • When should I NOT use LCS? If the problem explicitly requires contiguous matching (e.g., longest common substring) or involves edit operations with costs other than simple insert/delete, a different DP or greedy approach may be needed.

  • Can I improve the O(m·n) time for large inputs? For most interview scenarios the quadratic bound is acceptable. Specialized algorithms exist for very large inputs, but they add complexity and are rarely expected.

  • How do I handle more than two sequences? The multi‑sequence LCS problem generalizes to higher dimensions, but the DP becomes exponential in the number of sequences. Interviews typically restrict you to two.

  • What if the strings contain Unicode characters? Treat each Unicode code point as a single element; the DP does not change. Just be careful with language‑specific string handling to avoid accidental byte‑level splitting.

Frequently asked questions

When should I not use LCS?

Avoid LCS when the problem asks for a contiguous block (longest common substring) or when operation costs differ, such as weighted edits. In those cases a different DP or greedy algorithm is appropriate.

Can I improve the O(m·n) time for large inputs?

For interview‑scale inputs the quadratic solution is fine. Advanced algorithms like Hunt‑Szymanski exist for very large data, but they add complexity and are rarely required in interviews.

How do I reconstruct the actual subsequence?

Backtrack from `dp[m][n]` toward the origin: if `A[i-1]==B[j-1]` add that character and move diagonally; otherwise move to the larger neighbor (`dp[i-1][j]` or `dp[i][j-1]`). This yields the LCS in reverse order.

What if the inputs are Unicode strings?

Treat each Unicode code point as a single element. The DP logic stays the same; just ensure your language’s string iteration respects full characters rather than bytes.

#coding pattern#longest common subsequence#dynamic programming#interview prep#practice problems