0
0
DSA Typescriptprogramming

Longest Common Substring in DSA Typescript

Choose your learning style9 modes available
Mental Model
Find the longest sequence of characters that appears exactly the same and continuously in both strings.
Analogy: Imagine two friends comparing their favorite songs to find the longest exact part of the melody they both like without breaks.
String1: a b c d e f g
String2: x y c d e z

We look for the longest continuous matching part: c d e
Dry Run Walkthrough
Input: string1: "abcde", string2: "abfde"
Goal: Find the longest continuous substring common to both strings
Step 1: Initialize a 2D array dp with zeros for lengths of substrings
dp matrix (rows for string1, cols for string2) all zeros
Why: We need a table to store lengths of matching substrings ending at each pair of indices
Step 2: Compare characters at string1[0] and string2[0] ('a' and 'a'), they match, set dp[1][1] = 1
dp[1][1] = 1, rest zeros
Why: Matching characters start a substring of length 1
Step 3: Compare string1[1] and string2[1] ('b' and 'b'), match, dp[2][2] = dp[1][1] + 1 = 2
dp[2][2] = 2, dp[1][1] = 1, others zero
Why: Extend previous matching substring by 1
Step 4: Compare string1[2] and string2[2] ('c' and 'f'), no match, dp[3][3] = 0
dp[3][3] = 0
Why: Mismatch breaks the substring
Step 5: Compare string1[4] and string2[4] ('e' and 'e'), match, dp[5][5] = dp[4][4] + 1 = 1
dp[5][5] = 1
Why: Start new matching substring of length 1
Result:
Longest common substring length is 2 with substring "ab"
Annotated Code
DSA Typescript
class LongestCommonSubstring {
  static find(s1: string, s2: string): {length: number; substring: string} {
    const m = s1.length;
    const n = s2.length;
    const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
    let maxLen = 0;
    let endIndex = 0;

    for (let i = 1; i <= m; i++) {
      for (let j = 1; j <= n; j++) {
        if (s1[i - 1] === s2[j - 1]) {
          dp[i][j] = dp[i - 1][j - 1] + 1; // extend matching substring
          if (dp[i][j] > maxLen) {
            maxLen = dp[i][j]; // update max length
            endIndex = i; // track end index of substring in s1
          }
        } else {
          dp[i][j] = 0; // reset on mismatch
        }
      }
    }

    const substring = s1.substring(endIndex - maxLen, endIndex);
    return { length: maxLen, substring };
  }
}

// Driver code
const s1 = "abcde";
const s2 = "abfde";
const result = LongestCommonSubstring.find(s1, s2);
console.log(`Length: ${result.length}`);
console.log(`Substring: ${result.substring}`);
if (s1[i - 1] === s2[j - 1]) {
Check if characters match to extend substring
dp[i][j] = dp[i - 1][j - 1] + 1;
Extend length of matching substring by 1
if (dp[i][j] > maxLen) {
Update max length and end index if longer substring found
dp[i][j] = 0;
Reset length on mismatch
OutputSuccess
Length: 2 Substring: ab
Complexity Analysis
Time: O(m*n) because we compare each character of first string with each character of second string once
Space: O(m*n) because we store lengths of substrings in a 2D table of size m+1 by n+1
vs Alternative: Naive approach checks all substrings and is O(m^2*n^2), so DP is much faster
Edge Cases
One or both strings empty
Returns length 0 and empty substring
DSA Typescript
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
No common substring
Returns length 0 and empty substring
DSA Typescript
if (s1[i - 1] === s2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = 0; }
Strings are identical
Returns full string length and string
DSA Typescript
if (dp[i][j] > maxLen) { maxLen = dp[i][j]; endIndex = i; }
When to Use This Pattern
When asked to find the longest continuous matching part between two strings, use the Longest Common Substring pattern with dynamic programming to efficiently track matching lengths.
Common Mistakes
Mistake: Confusing longest common substring with longest common subsequence (which allows gaps)
Fix: Remember substring requires continuous characters; use DP that resets on mismatch
Mistake: Not resetting dp[i][j] to zero on mismatch, causing incorrect length counts
Fix: Explicitly set dp[i][j] = 0 when characters don't match
Summary
Finds the longest continuous substring common to two strings.
Use when you need the longest exact matching sequence without breaks.
Track lengths of matching substrings ending at each position and reset on mismatch.