Arian Soleimanzadeh
  • Home
  • Blog
  • Podcasts
  • Videos
  • Contact
العربيةArabic
DeutschGerman
EnglishEnglish
فارسیPersian
한국어Korean
中文Chinese
Panel•Quick Contact

Languages

Choose your interface locale

ar

العربية

Arabic

de

Deutsch

German

en

English

English

fa

فارسی

Persian

ko

한국어

Korean

zh

中文

Chinese

Get Appointment

Drop a quick message — I’ll reply as soon as possible.

LinkedInFast response
Home/Articles/Longest Common Substring Explained: Dynamic Programming, TypeScript and Real-World Uses
AlgorithmsArticle

Longest Common Substring Explained: Dynamic Programming, TypeScript and Real-World Uses

The Longest Common Substring problem finds the longest contiguous sequence shared by two strings. Learn the dynamic-programming approach, TypeScript implementation, complexity, optimization, and difference from LCS.

August 19, 20269 min read8 Views
#Algorithms#Longest Common Substring#String Algorithms#Dynamic Programming#TypeScript#Computer Science

Arian Soleimanzadeh

Software Engineer & Researcher

Longest Common Substring dynamic programming matrix highlighting the longest contiguous match between two strings

Arian Soleimanzadeh

AI · Code · Product

Research + Engineering
On this page
What is a substring?Another exampleBrute-force approachDynamic Programming definitionExample DP tableWhy the answer is not necessarily the final cellTypeScript implementationReturning the actual substringComplexitySpace optimizationLongest Common Substring vs. Longest Common SubsequenceDifference in the DP recurrenceText similarityCRM and data deduplicationBioinformaticsVersion and sequence comparisonPlagiarism and document similarityLog and security analysisMultiple optimal answersEdge casesCommon interview questionCommon mistake: using the LCS recurrenceAre faster algorithms possible?Conclusion

The Longest Common Substring problem asks us to find the longest contiguous sequence of characters that appears in two strings.

The word contiguous is essential.

Consider:

Code
12
ABABC
BABCA

The string:

Code
1
BABC

appears continuously in both inputs.

Therefore:

Code
12
Longest Common Substring = "BABC"
Length = 4

What is a substring?

A substring contains consecutive characters from the original string.

For:

Code
1
ABCDE

valid substrings include:

Code
1234
ABC
BCD
DE
C

But:

Code
1
ACE

is not a substring because its characters are not contiguous.


Another example

Consider:

Code
12
programming
gaming

A common contiguous section is:

Code
1
ming

so the longest common substring has length 4.


Brute-force approach

A straightforward solution is to generate every substring of the first string and check whether it appears in the second.

A string of length n has approximately:

Code
1
n × (n + 1) / 2

substrings.

Testing all of them quickly becomes expensive, so a dynamic-programming approach is usually preferred for standard algorithmic problems.


Dynamic Programming definition

For strings a and b, define:

Code
1
dp[i][j]

as the length of the longest common substring that ends exactly at a[i - 1] and b[j - 1].

If the two current characters match:

Code
1
a[i - 1] === b[j - 1]

then:

Code
1
dp[i][j] = dp[i - 1][j - 1] + 1

If they do not match:

Code
1
dp[i][j] = 0

The reset to zero is what enforces contiguity.


Example DP table

For:

Code
12
a = ABABC
b = BABCA

we obtain conceptually:

Code
123456
      B  A  B  C  A
A     0  1  0  0  1
B     1  0  2  0  0
A     0  2  0  0  1
B     1  0  3  0  0
C     0  0  0  4  0

The largest value is:

Code
1
4

which corresponds to:

Code
1
BABC

Why the answer is not necessarily the final cell

The best common substring can end anywhere in the two strings.

For this reason, we maintain a variable such as:

Code
1
maxLength

while filling the matrix.


TypeScript implementation

TypeScript
12345678910111213141516171819202122
function longestCommonSubstringLength(
  a: string,
  b: string
): number {
  const dp: number[][] = Array.from(
    { length: a.length + 1 },
    () => new Array(b.length + 1).fill(0)
  );

  let maxLength = 0;

  for (let i = 1; i <= a.length; i++) {
    for (let j = 1; j <= b.length; j++) {
      if (a[i - 1] === b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
        maxLength = Math.max(maxLength, dp[i][j]);
      }
    }
  }

  return maxLength;
}

For:

TypeScript
1
longestCommonSubstringLength("ABABC", "BABCA");

we get:

Code
1
4

Returning the actual substring

To return the substring itself, keep track of where the best match ends.

TypeScript
123456789101112131415161718192021222324252627
function longestCommonSubstring(
  a: string,
  b: string
): string {
  const dp: number[][] = Array.from(
    { length: a.length + 1 },
    () => new Array(b.length + 1).fill(0)
  );

  let maxLength = 0;
  let endIndex = 0;

  for (let i = 1; i <= a.length; i++) {
    for (let j = 1; j <= b.length; j++) {
      if (a[i - 1] === b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;

        if (dp[i][j] > maxLength) {
          maxLength = dp[i][j];
          endIndex = i;
        }
      }
    }
  }

  return a.slice(endIndex - maxLength, endIndex);
}

Complexity

For string lengths n and m:

Code
12
Time Complexity:  O(n × m)
Space Complexity: O(n × m)

Every pair of positions is examined once.


Space optimization

The current row depends only on the previous row.

Therefore, the full matrix is unnecessary when only the length is required.

Memory can be reduced to:

Code
1
O(min(n, m))

Example:

TypeScript
1234567891011121314151617181920212223242526
function longestCommonSubstringOptimized(
  a: string,
  b: string
): number {
  if (b.length > a.length) {
    [a, b] = [b, a];
  }

  let previous = new Array(b.length + 1).fill(0);
  let maxLength = 0;

  for (let i = 1; i <= a.length; i++) {
    const current = new Array(b.length + 1).fill(0);

    for (let j = 1; j <= b.length; j++) {
      if (a[i - 1] === b[j - 1]) {
        current[j] = previous[j - 1] + 1;
        maxLength = Math.max(maxLength, current[j]);
      }
    }

    previous = current;
  }

  return maxLength;
}

Longest Common Substring vs. Longest Common Subsequence

These problems are frequently confused.

Suppose:

Code
12
a = ABCDEF
b = ACEF

The Longest Common Subsequence can be:

Code
1
ACEF

because its characters only need to remain in order.

They do not need to be adjacent.

A common substring, however, must be contiguous. In this example:

Code
1
EF

is a valid common substring.


Difference in the DP recurrence

For Longest Common Substring, mismatch means:

Code
1
dp[i][j] = 0

For Longest Common Subsequence, mismatch normally leads to:

Code
1234
dp[i][j] = max(
  dp[i - 1][j],
  dp[i][j - 1]
)

That single difference reflects the requirement that a substring remain continuous.


Text similarity

A long exact contiguous match can be a useful signal when comparing strings or documents.

For example:

Code
12
customer_relationship_management
relationship_manager

contains a substantial common region.

Longest Common Substring alone is not a complete similarity metric, but it can be one feature in a larger matching system.


CRM and data deduplication

Suppose imported CRM data contains:

Code
12
Arian Soleimanzadeh Software Engineer
Arian Soleimanzadeh

A large common substring may provide evidence that the records are related.

Production systems should combine this with other signals such as:

  • Levenshtein Distance
  • Email matching
  • Phone matching
  • Token similarity
  • Data normalization

Bioinformatics

DNA and protein sequences can be treated as strings.

For example:

Code
12
ACGTACGT
TTGTACGG

contains a long shared contiguous region.

Longest Common Substring concepts can help identify similar local sequence regions, although practical bioinformatics systems often use specialized sequence-alignment algorithms.


Version and sequence comparison

Consider two API paths:

Code
12
/api/v1/customer/profile
/api/v2/customer/profile

A large common region is:

Code
1
/customer/profile

Common-substring logic can therefore be useful as a building block in sequence comparison tools.


Plagiarism and document similarity

Long identical segments between documents can provide a signal for copied or repeated content.

Real plagiarism-detection systems generally combine this idea with tokenization, N-grams, fingerprinting, and semantic techniques.


Log and security analysis

Consider:

Code
12
ERROR_AUTH_TOKEN_EXPIRED_USER_101
ERROR_AUTH_TOKEN_EXPIRED_USER_205

A long common segment can help identify that the entries belong to the same underlying error pattern.


Multiple optimal answers

There may be several longest common substrings with the same length.

For example:

Code
12
a = abcXYZ123
b = abcABC123

both:

Code
12
abc
123

have length 3.

If every answer is needed, store the end positions of all cells whose value equals the maximum.


Edge cases

Important cases include:

  • One empty string
  • No common characters
  • Identical strings
  • Multiple equally long matches
  • Case-sensitive versus case-insensitive comparison

For case-insensitive matching, normalize the input before running the algorithm.


Common interview question

A typical problem asks:

Given two strings, find the length or value of their longest common substring.

A good explanation should cover:

  1. The substring must be contiguous.
  2. dp[i][j] represents a match ending at the current positions.
  3. A mismatch resets the current match to zero.
  4. A global maximum must be maintained.
  5. Time complexity is O(n × m).
  6. Memory can be optimized if necessary.

Common mistake: using the LCS recurrence

Writing:

Code
1
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

on mismatch solves the Longest Common Subsequence problem, not Longest Common Substring.

For substring matching, the correct mismatch behavior is:

Code
1
dp[i][j] = 0

Are faster algorithms possible?

Yes.

For very large input sizes, advanced structures such as:

  • Suffix Trees
  • Suffix Arrays
  • Suffix Automata

can provide more efficient solutions.

However, Dynamic Programming remains one of the clearest approaches for learning, interviews, and moderate input sizes.


Conclusion

The Longest Common Substring problem finds the longest contiguous sequence shared by two strings.

Its core recurrence is:

Code
1234
if a[i - 1] === b[j - 1]
    dp[i][j] = dp[i - 1][j - 1] + 1
else
    dp[i][j] = 0

The standard solution uses:

Code
12
Time:  O(n × m)
Space: O(n × m)

with memory optimization down to:

Code
1
O(min(n, m))

The key idea to remember is simple:

A substring must remain continuous, so any mismatch breaks the current matching chain.

On this page
What is a substring?Another exampleBrute-force approachDynamic Programming definitionExample DP tableWhy the answer is not necessarily the final cellTypeScript implementationReturning the actual substringComplexitySpace optimizationLongest Common Substring vs. Longest Common SubsequenceDifference in the DP recurrenceText similarityCRM and data deduplicationBioinformaticsVersion and sequence comparisonPlagiarism and document similarityLog and security analysisMultiple optimal answersEdge casesCommon interview questionCommon mistake: using the LCS recurrenceAre faster algorithms possible?Conclusion

Article details

Publication metadata, reading time and live view information.

Published

August 19, 2026

Updated

August 23, 2026

Reading time

9 min read

Views

8

Author

Arian Soleimanzadeh

Previous article

What Is Kosaraju's Algorithm and What Problem Does It Solve?

Next article

What Is a Palindrome? Two-Pointer Checking, TypeScript and Interview Patterns

Arian Soleimanzadeh

Personal portfolio focused on modern web engineering, UI systems, and practical AI products — clean code, clean design.

Quick Links

  • About
  • Blog
  • Contact

Contact

  • soleimanzadeh.a.work@gmail.com
  • soleimanzadeh.uni@gmail.com

Availability: Weekdays

Typically replies within 24h.

Newsletter

Get updates on posts, projects, and new releases.

© 2026 ariansoleimanzadeh.site — All rights reserved.

LinkedIn