Bird
Raised Fist0
DSA Goprogramming

Longest Common Subsequence in DSA Go

Choose your learning style10 modes available
Mental Model
Find the longest sequence of characters that appear in the same order in both strings, but not necessarily consecutively.
Analogy: Imagine two friends telling stories. The longest common subsequence is like the longest shared storyline they both remember in the same order, even if some parts are skipped.
String1: A B C D G H
String2: A E D F H R

LCS matrix (partial):
    Ø A E D F H R
Ø [0 0 0 0 0 0 0]
A [0 ↑ 0 0 0 0 0]
B [0 0 0 0 0 0 0]
C [0 0 0 0 0 0 0]
D [0 0 0 ↑ 0 0 0]
G [0 0 0 0 0 0 0]
H [0 0 0 0 0 ↑ 0]
Dry Run Walkthrough
Input: String1: "ABCDGH", String2: "AEDFHR"
Goal: Find the longest common subsequence between the two strings.
Step 1: Compare last characters 'H' and 'R', they differ, take max LCS excluding one character
LCS length so far: 0
Matrix updated for last chars: max(LCS('ABCDG', 'AEDFHR'), LCS('ABCDGH', 'AEDFH'))
Why: Because last chars differ, we try skipping one char from either string to find max LCS
Step 2: Compare 'H' and 'H', they match, add 1 to LCS of previous substrings
LCS length updated to 1 for substrings ending before these chars
Why: Matching chars add 1 to LCS length from previous smaller substrings
Step 3: Compare 'D' and 'D', they match, add 1 to LCS of previous substrings
LCS length updated to 2 for substrings ending before these chars
Why: Matching chars add 1 to LCS length from previous smaller substrings
Step 4: Compare 'C' and 'F', differ, take max LCS excluding one char
LCS length remains 2, choosing max from skipping one char
Why: No match, so we try skipping one char to find max LCS
Step 5: Compare 'B' and 'E', differ, take max LCS excluding one char
LCS length remains 2
Why: No match, so we try skipping one char to find max LCS
Step 6: Compare 'A' and 'A', match, add 1 to LCS of previous substrings
LCS length updated to 3
Why: Matching chars add 1 to LCS length from previous smaller substrings
Result:
Longest Common Subsequence length = 3
One LCS is "ADH"
Annotated Code
DSA Go
package main

import "fmt"

func longestCommonSubsequence(text1 string, text2 string) int {
    m, n := len(text1), len(text2)
    dp := make([][]int, m+1)
    for i := range dp {
        dp[i] = make([]int, n+1)
    }

    for i := 1; i <= m; i++ {
        for j := 1; j <= n; j++ {
            if text1[i-1] == text2[j-1] {
                dp[i][j] = dp[i-1][j-1] + 1
            } else {
                if dp[i-1][j] > dp[i][j-1] {
                    dp[i][j] = dp[i-1][j]
                } else {
                    dp[i][j] = dp[i][j-1]
                }
            }
        }
    }
    return dp[m][n]
}

func main() {
    text1 := "ABCDGH"
    text2 := "AEDFHR"
    result := longestCommonSubsequence(text1, text2)
    fmt.Printf("Longest Common Subsequence length = %d\n", result)
}
if text1[i-1] == text2[j-1] {
Check if current characters match to extend LCS
dp[i][j] = dp[i-1][j-1] + 1
Add 1 to LCS length from previous smaller substrings
if dp[i-1][j] > dp[i][j-1] {
Choose max LCS length by skipping one character from either string
dp[i][j] = dp[i-1][j]
Skip character from text1
dp[i][j] = dp[i][j-1]
Skip character from text2
OutputSuccess
Longest Common Subsequence length = 3
Complexity Analysis
Time: O(m*n) because we fill a 2D table of size m by n once
Space: O(m*n) because we store LCS lengths for all substring pairs
vs Alternative: Naive recursive approach is exponential O(2^(m+n)) due to repeated subproblems; DP avoids this by storing results
Edge Cases
One or both strings empty
LCS length is 0 because no common subsequence exists
DSA Go
dp := make([][]int, m+1) // handles zero length by initializing dp with zeros
Strings with no common characters
LCS length is 0 because no characters match
DSA Go
if text1[i-1] == text2[j-1] { ... } else { ... } // no matches lead to dp entries staying zero
Strings are identical
LCS length equals length of strings
DSA Go
dp[i][j] = dp[i-1][j-1] + 1 // increments for every matching character
When to Use This Pattern
When you need to find the longest sequence common to two sequences in order, use the Longest Common Subsequence pattern because it efficiently finds this using dynamic programming.
Common Mistakes
Mistake: Confusing subsequence with substring and expecting characters to be consecutive
Fix: Remember subsequence characters can be non-contiguous but must keep order
Mistake: Not initializing the DP table with zeros causing index errors
Fix: Initialize dp with size (m+1)*(n+1) filled with zeros to handle empty substrings
Mistake: Using recursion without memoization leading to exponential time
Fix: Use bottom-up DP table to store intermediate results and avoid repeated work
Summary
Finds the longest sequence of characters appearing in order in both strings.
Use when comparing two sequences to find their longest shared pattern without requiring contiguity.
The key insight is to build solutions for smaller substrings and combine them to solve the full problem.

Practice

(1/5)
1. What does the Longest Common Subsequence (LCS) between two strings represent?
easy
A. The longest substring that appears exactly the same in both strings.
B. The longest sequence of characters that appear in both strings in the same order, but not necessarily consecutively.
C. The shortest sequence that can be formed by combining both strings.
D. The total number of characters in both strings combined.

Solution

  1. Step 1: Understand the definition of LCS

    The LCS is the longest sequence that appears in both strings in the same order, but characters do not need to be next to each other.
  2. Step 2: Differentiate from substring

    A substring requires characters to be consecutive, but LCS allows gaps.
  3. Final Answer:

    The longest sequence of characters that appear in both strings in the same order, but not necessarily consecutively. -> Option B
  4. Quick Check:

    LCS = longest ordered sequence [OK]
Hint: LCS is about order, not consecutive characters [OK]
Common Mistakes:
  • Confusing LCS with longest substring
  • Thinking LCS characters must be consecutive
  • Mixing LCS with string concatenation
2. Which of the following Go function signatures correctly represents a function to compute the length of the LCS of two strings s1 and s2?
easy
A. func lcs(s1, s2 string) int
B. func lcs(s1, s2 int) string
C. func lcs(s1, s2 []int) int
D. func lcs(s1, s2 string) string

Solution

  1. Step 1: Identify input types for LCS function

    The LCS function takes two strings as input, so parameters should be of type string.
  2. Step 2: Identify output type for LCS function

    The function returns the length of the LCS, which is an integer.
  3. Final Answer:

    func lcs(s1, s2 string) int -> Option A
  4. Quick Check:

    Input strings, output int length [OK]
Hint: LCS inputs are strings, output is integer length [OK]
Common Mistakes:
  • Using wrong input types like int or slices
  • Returning string instead of length
  • Mixing input and output types
3. What is the output of the following Go code snippet?
package main
import "fmt"
func lcs(s1, s2 string) int {
    m, n := len(s1), len(s2)
    dp := make([][]int, m+1)
    for i := range dp {
        dp[i] = make([]int, n+1)
    }
    for i := 1; i <= m; i++ {
        for j := 1; j <= n; j++ {
            if s1[i-1] == s2[j-1] {
                dp[i][j] = dp[i-1][j-1] + 1
            } else {
                if dp[i-1][j] > dp[i][j-1] {
                    dp[i][j] = dp[i-1][j]
                } else {
                    dp[i][j] = dp[i][j-1]
                }
            }
        }
    }
    return dp[m][n]
}
func main() {
    fmt.Println(lcs("AGGTAB", "GXTXAYB"))
}
medium
A. 4
B. 5
C. 6
D. 7

Solution

  1. Step 1: Understand the input strings

    Strings are "AGGTAB" and "GXTXAYB". We find their LCS length using dynamic programming.
  2. Step 2: Trace the LCS calculation

    The LCS is "GTAB" which has length 4, but checking carefully the code and strings, the LCS is actually "GTAB" (length 4) or "GTTAB"? The correct LCS is "GTAB" length 4 or "GTAB" length 4? Actually, the LCS is "GTAB" length 4. But the code returns 4 or 5? Let's check carefully: The LCS is "GTAB" (G T A B) length 4. But the code returns dp[m][n], which is 4. But options have 4 and 5. Let's verify carefully: The LCS is "GTAB" length 4. So output is 4.
  3. Final Answer:

    4 -> Option A
  4. Quick Check:

    LCS length of "AGGTAB" and "GXTXAYB" = 4 [OK]
Hint: Trace DP table or recall classic LCS example [OK]
Common Mistakes:
  • Confusing LCS length with longest substring length
  • Off-by-one errors in indexing
  • Misreading input strings
4. The following Go code attempts to compute the LCS length but has a bug. What is the bug?
func lcs(s1, s2 string) int {
    m, n := len(s1), len(s2)
    dp := make([][]int, m)
    for i := range dp {
        dp[i] = make([]int, n)
    }
    for i := 1; i <= m; i++ {
        for j := 1; j <= n; j++ {
            if s1[i-1] == s2[j-1] {
                dp[i][j] = dp[i-1][j-1] + 1
            } else {
                if dp[i-1][j] > dp[i][j-1] {
                    dp[i][j] = dp[i-1][j]
                } else {
                    dp[i][j] = dp[i][j-1]
                }
            }
        }
    }
    return dp[m-1][n-1]
}
medium
A. The return statement should be dp[m][n] instead of dp[m-1][n-1].
B. The loops should start from 0 instead of 1.
C. The dp slice is not initialized with size m+1 and n+1, causing index out of range errors.
D. The comparison s1[i-1] == s2[j-1] should be s1[i] == s2[j].

Solution

  1. Step 1: Check dp slice initialization

    The dp slice is created with size m and n, but the loops access dp[i][j] for i and j up to m and n inclusive, causing index out of range errors.
  2. Step 2: Correct dp size for safe indexing

    dp should be initialized with size m+1 and n+1 to safely access dp[i][j] where i and j go up to m and n.
  3. Final Answer:

    The dp slice is not initialized with size m+1 and n+1, causing index out of range errors. -> Option C
  4. Quick Check:

    dp size must be m+1 x n+1 [OK]
Hint: dp size must cover all indices used in loops [OK]
Common Mistakes:
  • Initializing dp with size m and n instead of m+1 and n+1
  • Starting loops at 1 but dp size too small
  • Incorrect return index
5. Given two strings s1 = "ABCBDAB" and s2 = "BDCABA", write a Go function that returns the actual Longest Common Subsequence string, not just its length. Which approach correctly reconstructs the LCS from the DP table?
hard
A. Sort both strings and find common characters in sorted order.
B. Build the DP table and return dp[m][n] as the LCS string directly.
C. Use recursion without memoization to find the LCS string.
D. Build the DP table for LCS length, then backtrack from dp[m][n] moving diagonally when characters match, else move in direction of larger dp value.

Solution

  1. Step 1: Build DP table for LCS length

    Use dynamic programming to fill dp table with lengths of LCS for substrings of s1 and s2.
  2. Step 2: Backtrack to reconstruct LCS string

    Start from dp[m][n], move diagonally up-left if characters match (add character to result), else move in direction of larger dp value until reaching dp[0][0].
  3. Final Answer:

    Build the DP table for LCS length, then backtrack from dp[m][n] moving diagonally when characters match, else move in direction of larger dp value. -> Option D
  4. Quick Check:

    DP + backtracking reconstructs LCS string [OK]
Hint: Backtrack DP table diagonally on matches to get LCS string [OK]
Common Mistakes:
  • Returning dp[m][n] as string instead of length
  • Not backtracking to reconstruct sequence
  • Sorting strings loses original order
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard