← Back to list

Solving LeetCode 1143: Longest Common Subsequence — An Intuitive Guide For Beginner

In this article, we’ll solve LeetCode problem 1143: Longest Common Subsequence. What make this article be different:

Eka Gunawan · 2026-02-12 02:12 · 0 claps · 5.2 min read
#data-structures #algorithms #leetcode #lcs #recursion
Open on Medium ↗
Wiki topics: 💻 · Programming

Solving LeetCode 1143: Longest Common Subsequence — An Intuitive Guide For Beginner

In this article, we’ll solve **LeetCode problem 1143: Longest Common Subsequence. **What make this article be different:

beginner friendly, fix confusion, avoid traps

LeetCode 1143 asks:

Given two strings text1 and text2, return the length of their Longest Common Subsequence (LCS).

A subsequence means you can delete characters without changing the relative order. Example: “abcde” and “ace” → LCS length = 3 (“ace”).

This problem is basically: “Walk through both strings and make the best choices without getting lost.

OUTLINE

1. Step by Step Code Implementation 2. Common Traps (and Fixes) 3. Common Confusions (and Fixes) 4. Edge Cases

1. Code Implementation (Python)

At any moment, you are standing at:

  • index i in text1
  • index j in text2

Define:

dfs(i, j) = the LCS length between text1[i:] and text2[j:]

What does (i, j) represent?

  • i = current index in text1
  • j = current index in text2

So (i, j) uniquely defines:

The best LCS we can build starting from here.

So we have to return dfs(0, 0) → the best LCS we can build starting from (0,0)

Step 1 — Base case (when the game ends)

If either pointer reaches the end, there’s nothing left to match:

# base case
if i == len(text1) or j == len(text2) → return 0

Step 2 — If characters match, take it

If text1[i] == text2[j], that character can be part of the subsequence.

So:

  • we count it → 1
  • and move both pointers forward → dfs(i+1, j+1)
if text1[i] == text2[j]:
    return 1 + dfs(i + 1, j + 1) # A match. Count it in and move on.

Step 3 — If characters don’t match, we must “skip” something

If text1[i] != text2[j], we can’t take both at the same time.

So we try both possibilities and PICK THE BEST (max):

  • skip text1[i] → dfs(i+1, j)
  • skip text2[j] → dfs(i, j+1)
  • pick the best
return max(dfs(i + 1, j), dfs(i, j + 1))

Step 4 — Putting it together

def longestCommonSubsequence(self, text1: str, text2: str) -> int:

    def dfs(i, j):
        if i == len(text1) or j == len(text2):
            return 0              ✅ # 1. case: either pointer reach the end

        if text1[i] == text2[j]:        ✅ # 2. case: character match
            return 1 + dfs(i + 1, j + 1)   # count & move both pointer 

        return max(dfs(i + 1, j), dfs(i, j + 1))
               ✅ # 3. case: characters don't match

    return dfs(0, 0)

# This is naive solution

Time Complexity

Let:

  • m = len(text1)
  • n = len(text2)

In the worst case (few matches), each state branches into 2 calls: O(2^(m+n)) (exponential)

Space Complexity

recursion depth up to m + n → O(m + n) (call stack)

🚨 Warning

Despite intuitive, this naive solution times out: the hidden repetition. The recursion keeps recomputing the same states (i, j) again and again.

Example: dfs(3, 5) might be reached from:

  • dfs(2,5) → skip text1
  • dfs(3,4) → skip text2

and many other paths

So you’re solving the same subproblem repeatedly.

This is the classic sign that the problem is Dynamic Programming (DP).

✅ Fix: Memoization (same idea + remember answers)

We keep a cache:

  • key = (i, j)
  • value = dfs(i, j)

If we’ve computed it once, we reuse it.

def longestCommonSubsequence(self, text1: str, text2: str) -> int:
    memo = {}                   ✅ # add a memo dictionary

    def dfs(i, j):
        if i == len(text1) or j == len(text2):
            return 0

         if (i, j) in memo:      
             return memo[(i, j)]    

        if text1[i] == text2[j]:
            memo[(i, j)] = 1 + dfs(i + 1, j + 1)
        else:
            memo[(i, j)] = max(dfs(i + 1, j), dfs(i, j + 1))

        return memo[(i, j)]

    return dfs(0, 0)

# This is optimized solution

Time Complexity

  • There are only m * n unique states (i, j).
  • Each state is computed once, and work inside is O(1), so: O(m * n)

Space Complexity

  • O(m * n) for memo
  • plus O(m + n) call stack
  • Overall: O(m * n) dominates

2. Common Traps (and fixes)

❌ Trap 1: “Subsequence” vs “Substring”

  • Substring = continuous chunk
  • Subsequence = can skip characters

✅ Fix: Repeat this rule:

LCS allows skipping; order must remain.

❌ Trap 2: Returning 1 + max(…) on mismatch

Some beginners do:

return 1 + max(dfs(i+1,j), dfs(i,j+1))

# ❌ incorrect

That’s wrong because mismatch does not add a matched character.

✅ Fix: only add +1 when text1[i] == text2[j]

return max(dfs(i+1,j), dfs(i,j+1))

# ✅ Correct

Any other traps you know ?

Comment down below ⬇️ ⬇️ ⬇️

3. Common Beginner Confusion (and Fix)

❌ Confusion 1:

“Why do we try both skips?”

✅ Answer: Because when text1[i] != text2[j], you don’t know which character is “blocking” the best subsequence.

  • Skipping text1[i] might reveal a match later.
  • Skipping text2[j] might reveal a different match later.

FIX mental model: when mismatch happens, you’re choosing which string to “advance” to find a better alignment.

❌ Confusion 2:

“Why does memoization make it O(mn)?”*

✅ Answer: Because (i, j) fully describes the remaining problem: text1[i:] and text2[j:].

There are only:

  • m possible i
  • n possible j

so m*n possible states

DP is just “don’t solve the same state twice.”

❌ Confusion 3:

“What does dfs(i, j) actually represent?”

✅ Answer:

  • It’s NOT “the answer so far”.
  • It’s “the best answer you can still achieve from here”.

✅ FIX phrase:

dfs(i, j) = best LCS length for the suffixes starting at i and j.

Do you have any other confusions? Just comment down below ⬇️⬇️⬇️

4. Edge Case Handling

Even though the logic is clean, interviewers love edge cases, like below:

  • One or both string are empty
  • Identical strings
  • No characters match at all
  • One string is much longer than the other
  • Repeated characters

Other possible edge cases in mind?

Let’s discuss on the comment section ⬇️⬇️⬇️

Let’s go through them carefully.

Edge Case 1 — One or both string are empty

Example:

text1 = ""
text2 = "abc"

or

text1 = ""
text2 = ""

Does our solution handle this?

Yes, because:

if i == len(text1) or j == len(text2): return 0

If text1 is empty, len(text1) == 0, so dfs(0, 0) immediately returns 0.

✅ No special handling needed.

Edge Case 2 — Identical strings

Example:

text1 = "abcde"
text2 = "abcde"

Expected result: 5

Does our solution handle this?

Every character matches, we always go into:

return 1 + dfs(i+1, j+1)

So it walks diagonally until the end.

✅ Works perfectly.

Edge Case 3 — No characters match at all

Example:

text1 = "abc"
text2 = "xyz"

Expected result: 0

Does our solution handle this?

Every comparison is mismatch, so we keep exploring:

max(dfs(i+1, j), dfs(i, j+1))

Eventually all paths hit base case and return 0.

✅ Correct output = 0

Edge Case 4 — One string is much longer than the other

Example:

text1 = "aaaaaaaaaa"
text2 = "aa"

Expected result: 2

Even though text1 has many “a”s, text2 only has two.

Does our solution handle this?

The recursion ensures we can only count matches when both pointers move.

So we cannot “overcount” characters.

The structure:

Match → move both pointers

Mismatch → explore skipping

ensures correctness.

✅ Safe.

Edge Case 5— Repeated Characters

Example:

text1 = "abcba"
text2 = "abcbcba"

Beginners sometimes worry:

“Will it accidentally reuse the same character twice?”

Answer: No.

Because each recursive call strictly moves forward in indices:

  • i never decreases
  • j never decreases

So each character position can only be used once.

That’s guaranteed by how we define the state (i, j).

✅ No duplication possible.

Thanks for reading! Hopefully you got the core idea.

Comments down ⬇️ below️️ if you have any questions. I will be glad to reply to every questions.

Let’s stay in touch:

🔗 Medium: Follow → Eka Gunawan

🔗 LinkedIn: Connect for career and job strategy → [linkedin.com/in/eka-gun-tw/]

Found this helpful? Give it a clap and follow for more algorithm deep-dives!


메타데이터
post_id
fe4a0bcb8eb5
slug
solving-leetcode-1143-longest-common-subsequence-an-intuitive-guide-for-beginner-fe4a0bcb8eb5
url
https://medium.com/@send.raden/solving-leetcode-1143-longest-common-subsequence-an-intuitive-guide-for-beginner-fe4a0bcb8eb5
canonical_url
https://medium.com/@send.raden/solving-leetcode-1143-longest-common-subsequence-an-intuitive-guide-for-beginner-fe4a0bcb8eb5
author_url
https://medium.com/@send.raden
status
ok
fetched_at
2026-06-10 08:17:25