Master string matching and similarity algorithms — Longest Common Subsequence (LCS) with DP, Edit Distance (Levenshtein), Longest Common Substring, Z-algorithm for pattern matching, and their applications in spell checking, DNA analysis, and diff utilities.
Longest Common Subsequence (LCS)
Definition: The longest sequence of characters that appears in both strings (not necessarily contiguous).
s1 = "ABCBDAB"
s2 = "BDCABA"
LCS = "BCAB" or "BCBA" — length 4
DP Solution: O(m×n)
def lcs_length(s1, s2):
"""
Find length of LCS using DP table.
dp[i][j] = LCS of s1[:i] and s2[:j]
"""
m, n = len(s1), len(s2)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1 # Characters match
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) # Take best of skipping either
return dp[m][n]
def lcs_string(s1, s2):
"""Reconstruct the actual LCS string."""
m, n = len(s1), len(s2)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
# Backtrack to find the sequence
result = []
i, j = m, n
while i > 0 and j > 0:
if s1[i-1] == s2[j-1]:
result.append(s1[i-1])
i -= 1; j -= 1
elif dp[i-1][j] > dp[i][j-1]:
i -= 1
else:
j -= 1
return ''.join(reversed(result))
print(lcs_length("ABCBDAB", "BDCABA")) # 4
print(lcs_string("ABCBDAB", "BDCABA")) # "BCAB"
Time: O(m×n), Space: O(m×n)
Space-optimized to O(min(m,n)):
def lcs_space_optimized(s1, s2):
if len(s1) < len(s2): s1, s2 = s2, s1 # s1 is longer
prev = [0] * (len(s2) + 1)
for char in s1:
curr = [0] * (len(s2) + 1)
for j, c2 in enumerate(s2, 1):
curr[j] = prev[j-1] + 1 if char == c2 else max(prev[j], curr[j-1])
prev = curr
return prev[-1]
Longest Common Substring
Note: Different from LCS — substring must be contiguous.
s1 = "ABCBDAB"
s2 = "BDCABA"
LCS (subsequence) = "BCAB" (length 4)
Longest Common Substring = "AB" or "BD" or "BA" (length 2)
def longest_common_substring(s1, s2):
"""
Find length and value of longest common contiguous substring.
dp[i][j] = length of longest common substring ending at s1[i-1] and s2[j-1]
"""
m, n = len(s1), len(s2)
dp = [[0] * (n+1) for _ in range(m+1)]
max_len = 0
end_pos = 0 # End index in s1
for i in range(1, m+1):
for j in range(1, n+1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
if dp[i][j] > max_len:
max_len = dp[i][j]
end_pos = i
else:
dp[i][j] = 0 # Contiguous — reset on mismatch
return s1[end_pos - max_len : end_pos], max_len
print(longest_common_substring("ABCBDAB", "BDCABA")) # ('AB', 2) or similar
print(longest_common_substring("ABABC", "BABCAB")) # ('BABC', 4)
Z-Algorithm: O(n+m)
Builds a Z-array where Z[i] = length of longest substring starting at position i that is also a prefix of the string.
def z_function(s):
"""
Compute Z-array for string s.
Z[i] = length of longest substring starting at s[i] matching prefix of s.
Z[0] = len(s) by convention.
Time: O(n), Space: O(n)
"""
n = len(s)
z = [0] * n
l, r = 0, 0
for i in range(1, n):
if i < r:
z[i] = min(r - i, z[i - l])
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
z[i] += 1
if i + z[i] > r:
l, r = i, i + z[i]
z[0] = n # Convention
return z
def z_search(text, pattern):
"""
Search using Z-algorithm.
Concatenate: combined = pattern + "$" + text
Z values equal to m at indices > m indicate a match.
"""
m = len(pattern)
combined = pattern + "$" + text
z = z_function(combined)
matches = []
for i in range(m + 1, len(combined)):
if z[i] == m:
matches.append(i - m - 1) # Position in text
return matches
print(z_search("aabxaabxcaabxaabxay", "aabx")) # [0, 4, 10, 14]
Comparison Summary
| Problem | Algorithm | Time | Space |
|---|
| Pattern search | KMP, Z-algo | O(n+m) | O(m) |
| String similarity | Edit distance | O(mn) | O(mn) |
| Common subsequence | LCS DP | O(mn) | O(mn) |
| Common substring | DP | O(mn) | O(mn) |
| Fuzzy match | Edit distance | O(mn) | O(mn) |
Applications
| Spell checker | Edit distance between typed word and dictionary |
| DNA alignment | LCS of DNA sequences |
| Git diff | LCS-based to find changed lines |
| Autocomplete | Z-algorithm or KMP for prefix matching |
| Plagiarism | Longest common substring detection |
| Search engines | Multiple pattern matching (Aho-Corasick) |
*Next: Palindrome Problems*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for String Matching — Longest Common Subsequence, Edit Distance, and Z-Algorithm.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, strings, string, matching