Python Program to Find Longest Common Substring using Dynamic Programming with Bottom-Up Approach

Finding the longest common substring using dynamic programming with a bottom-up approach involves building a solution by solving smaller subproblems first. This method uses a 2D table to store intermediate results, avoiding redundant calculations and ensuring optimal performance.

The algorithm works by comparing characters from both strings and building up the solution from the bottom of the problem space ?

Algorithm Overview

The bottom-up approach creates a 2D table where val[i][j] represents the length of the common substring ending at position i in the first string and position j in the second string. We fill this table from the bottom-right corner moving towards the top-left.

Example

def compute_lcw(string_1, string_2):
    # Create a 2D table initialized with -1
    val = [[-1] * (len(string_2) + 1) for _ in range(len(string_1) + 1)]
    
    # Initialize base cases
    for i in range(len(string_1) + 1):
        val[i][len(string_2)] = 0
    for j in range(len(string_2)):
        val[len(string_1)][j] = 0
    
    # Variables to track the longest substring
    lcw_i = lcw_j = -1
    lcw_len = 0
    
    # Fill the table from bottom-right to top-left
    for i in range(len(string_1) - 1, -1, -1):
        for j in range(len(string_2) - 1, -1, -1):
            if string_1[i] != string_2[j]:
                val[i][j] = 0
            else:
                val[i][j] = 1 + val[i + 1][j + 1]
                if lcw_len < val[i][j]:
                    lcw_len = val[i][j]
                    lcw_i = i
                    lcw_j = j
    
    return lcw_len, lcw_i, lcw_j

# Test the function
string_1 = 'bull'
string_2 = 'bullied'
lcw_len, lcw_i, lcw_j = compute_lcw(string_1, string_2)

print("The longest common substring is:")
if lcw_len > 0:
    print(string_1[lcw_i:lcw_i + lcw_len])
    print(f"Length: {lcw_len}")
    print(f"Position in string_1: {lcw_i}")
    print(f"Position in string_2: {lcw_j}")
else:
    print("No common substring found")

The output of the above code is ?

The longest common substring is:
bull
Length: 4
Position in string_1: 0
Position in string_2: 0

How It Works

The algorithm follows these key steps:

  • Table Creation: A 2D table val[i][j] stores the length of common substring ending at positions i and j
  • Base Cases: Initialize boundary conditions where one string is exhausted (length = 0)
  • Character Comparison: If characters match, extend the substring length by 1 plus the diagonal value
  • Tracking Maximum: Keep track of the longest substring found and its starting positions
  • Bottom-Up Fill: Process from the end of both strings towards the beginning

Another Example

def compute_lcw(string_1, string_2):
    val = [[-1] * (len(string_2) + 1) for _ in range(len(string_1) + 1)]
    
    for i in range(len(string_1) + 1):
        val[i][len(string_2)] = 0
    for j in range(len(string_2)):
        val[len(string_1)][j] = 0
    
    lcw_i = lcw_j = -1
    lcw_len = 0
    
    for i in range(len(string_1) - 1, -1, -1):
        for j in range(len(string_2) - 1, -1, -1):
            if string_1[i] != string_2[j]:
                val[i][j] = 0
            else:
                val[i][j] = 1 + val[i + 1][j + 1]
                if lcw_len < val[i][j]:
                    lcw_len = val[i][j]
                    lcw_i = i
                    lcw_j = j
    
    return lcw_len, lcw_i, lcw_j

# Test with different strings
string_1 = 'programming'
string_2 = 'algorithm'
lcw_len, lcw_i, lcw_j = compute_lcw(string_1, string_2)

print("Strings:", string_1, "and", string_2)
print("Longest common substring:", string_1[lcw_i:lcw_i + lcw_len] if lcw_len > 0 else "None")
print("Length:", lcw_len)
Strings: programming and algorithm
Longest common substring: gram
Length: 4

Time and Space Complexity

This dynamic programming approach has:

  • Time Complexity: O(m × n) where m and n are the lengths of the two strings
  • Space Complexity: O(m × n) for the 2D table storage

Conclusion

The bottom-up dynamic programming approach efficiently finds the longest common substring by building solutions from smaller subproblems. This method ensures optimal time complexity while maintaining clear logic for substring identification.

Updated on: 2026-03-25T17:21:29+05:30

374 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements