Longest Common Substring (original) (raw)

Given two strings **s1 and **s2, find the **length of the longest common substring. A **substring is a sequence of characters that appears **contiguously in a string.

**Example:

**Input: s1 = "GeeksforGeeks", s2 = "GeeksQuiz"
**Output : 5
**Explanation: The longest common substring is "Geeks" and is of length 5.

**Input: s1 = "abcdxyz", s2 = "xyzabcd"
**Output : 4
**Explanation: The longest common substring is "abcd" and is of length 4.

**Input: s1 = "abc", s2 = ""
**Output : 0

Table of Content

[Naive Approach] Recursion - O(m×n×min(m,n)) Time and O(min(m,n)) Space

The idea is to consider every pair of indexes (i, j) and find the longest common substring ending at i in s1 and j in s2. In other words, we find the longest common suffix ending at every pair(i, j). At the end, we return length of the max of all longest common suffixes. To find length of the common suffix ending at i and j, we recursively use the lengths of the longest common suffixes ending at (i-1) and (j-1).

Length of the Longest Common Substring = Max of lengths of all Longest Common Suffixes

Length of the Longest Common Suffix Ending at (i, j) = Length of Longest Common Suffix Ending at (i-1, j-1) + 1 if s1[i] == s2[j], else 0

C++ `

#include #include using namespace std;

// Returns length of the longest common suffix // ending with the last characters int commonSuffix(string& s1, string& s2, int m, int n) { if (m == 0 || n == 0 || s1[m - 1] != s2[n - 1]) return 0; return 1 + commonSuffix(s1, s2, m - 1, n - 1); }

int longCommSubstr(string& s1, string& s2) { int res = 0;

// Find the longest common substring 
// and take the maximum of all.
for (int i = 1; i <= s1.size(); i++) {
    for (int j = 1; j <= s2.size(); j++) {
        res = max(res, commonSuffix(s1, s2, i, j));
    }
}
return res;

}

int main() { string s1 = "GeeksforGeeks"; string s2 = "GeeksQuiz"; cout << longCommSubstr(s1, s2) << endl; return 0; }

C

#include <stdio.h> #include <string.h> #include <stdlib.h>

// Returns length of the longest common suffix // ending with the last characters int commonSuffix(char* s1, char* s2, int m, int n) { if (m == 0 || n == 0 || s1[m - 1] != s2[n - 1]) return 0; return 1 + commonSuffix(s1, s2, m - 1, n - 1); }

int longCommSubstr(char* s1, char* s2) { int res = 0; int m = strlen(s1); int n = strlen(s2);

// Find the longest common substring 
// and take the maximum of all.
for (int i = 1; i <= m; i++) {
    for (int j = 1; j <= n; j++) {
        res = res > commonSuffix(s1, s2, i, j) ? res : commonSuffix(s1, s2, i, j);
    }
}
return res;

}

int main() { char s1[] = "GeeksforGeeks"; char s2[] = "GeeksQuiz"; printf("%d\n", longCommSubstr(s1, s2)); return 0; }

Java

class GfG {

// Returns length of the longest common suffix 
// ending with the last characters
static int commonSuffix(String s1, String s2, int m, int n) {
    if (m == 0 || n == 0 || s1.charAt(m - 1) 
                              != s2.charAt(n - 1)) {
        return 0;
    }
    return 1 + commonSuffix(s1, s2, m - 1, n - 1);
}

static int longCommSubstr(String s1, String s2) {
    int res = 0;
    int m = s1.length();
    int n = s2.length();

    // Find the longest common substring  
    // and take the maximum of all.
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            res = Math.max(res, commonSuffix(s1, s2, i, j));
        }
    }
    return res;
}

public static void main(String[] args) {
    String s1 = "GeeksforGeeks";
    String s2 = "GeeksQuiz";
    System.out.println(longCommSubstr(s1, s2));
}

}

Python

Returns length of the longest common suffix

ending with the last characters

def commonSuffix(s1, s2, m, n): if m == 0 or n == 0 or s1[m - 1] != s2[n - 1]: return 0 return 1 + commonSuffix(s1, s2, m - 1, n - 1)

def longCommSubstr(s1, s2): res = 0 m = len(s1) n = len(s2)

# Find the longest common substring 
# and take the maximum of all.
for i in range(1, m + 1):
    for j in range(1, n + 1):
        res = max(res, commonSuffix(s1, s2, i, j))
return res

if name == "main": s1 = "GeeksforGeeks" s2 = "GeeksQuiz" print(longCommSubstr(s1, s2))

C#

using System;

class GfG {

// Returns length of the longest suffix 
// ending with the last characters
static int commonSuffix(string s1, string s2, int m, int n) {
    if (m == 0 || n == 0 || s1[m - 1] != s2[n - 1]) {
        return 0;
    }
    return 1 + commonSuffix(s1, s2, m - 1, n - 1);
}

static int longCommSubstr(string s1, string s2) {
    int res = 0;
    int m = s1.Length;
    int n = s2.Length;

    // Find the longest common substring  
    // at every pair of characters 
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            res = Math.Max(res, commonSuffix(s1, s2, i, j));
        }
    }
    return res;
}

static void Main() {
    string s1 = "GeeksforGeeks";
    string s2 = "GeeksQuiz";
    Console.WriteLine(longCommSubstr(s1, s2));
}

}

JavaScript

// Returns length of the longest common substring // ending with the last characters function commonSuffix(s1, s2, m, n) { if (m === 0 || n === 0 || s1[m - 1] !== s2[n - 1]) { return 0; } return 1 + commonSuffix(s1, s2, m - 1, n - 1); }

function longCommSubstr(s1, s2) { let res = 0; let m = s1.length; let n = s2.length;

// Find the longest common substring 
// and take the maximum of all.
for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
        res = Math.max(res, commonSuffix(s1, s2, i, j));
    }
}
return res;

}

//Driver Code let s1 = "GeeksforGeeks"; let s2 = "GeeksQuiz"; console.log(longCommSubstr(s1, s2));

`

[Better Approach] Using Dynamic Programming - O(m×n) Time and O(m×n) Space

From the recursive approach we can observe that the same (i, j) states are solved repeatedly while computing common suffixes, leading to overlapping subproblems. So,

Say the strings are **S1 = "ABCD" and **S2 = "ACDG", Follow below :

C++ `

#include #include #include using namespace std;

int longCommSubstr(string& s1, string& s2) { int m = s1.length(); int n = s2.length();

// Create a table to store lengths of longest
// common suffixes of substrings.
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

// Build dp[m+1][n+1] in bottom-up fashion.    
int res = 0;
for (int i = 1; i <= m; i++) {
    for (int j = 1; j <= n; j++) {
        if (s1[i - 1] == s2[j - 1]) {
            dp[i][j] = dp[i - 1][j - 1] + 1;
            res = max(res, dp[i][j]);
        } else {
            dp[i][j] = 0;
        }
    }
}
return res;

}

int main() {

string s1 = "GeeksforGeeks";
string s2 = "GeeksQuiz";
cout << longCommSubstr(s1, s2) << endl;
return 0;

}

C

#include <stdio.h> #include <string.h>

int longCommSubstr(char* s1, char* s2) { int m = strlen(s1); int n = strlen(s2);

// Create a table to store lengths of longest
// common suffixes of substrings.
int dp[m + 1][n + 1];

// Build dp[m+1][n+1] in bottom-up fashion.    
int res = 0;
for (int i = 1; i <= m; i++) {
    for (int j = 1; j <= n; j++) {
        if (s1[i - 1] == s2[j - 1]) {
            dp[i][j] = dp[i - 1][j - 1] + 1;
            if (dp[i][j] > res) {
                res = dp[i][j];
            }
        } else {
            dp[i][j] = 0;
        }
    }
}

return res;

}

int main() {

char s1[] = "GeeksforGeeks";
char s2[] = "GeeksQuiz";
printf("%d\n", longCommSubstr(s1, s2));
return 0;

}

Java

class GFG {

public static int longCommSubstr(String s1, String s2) {
    int m = s1.length();
    int n = s2.length();

    // Create a table to store lengths of longest
    // common suffixes of substrings
    int[][] dp = new int[m + 1][n + 1];
    int res = 0; 

    // Build dp[m+1][n+1] in bottom up fashion.
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                res = Math.max(res, dp[i][j]);
            } else {
                dp[i][j] = 0;
            }
        }
    }
    return res;
}

public static void main(String[] args) {
    
    String s1 = "GeeksforGeeks";
    String s2 = "GeeksQuiz";
    System.out.println(longCommSubstr(s1, s2));
}

}

Python

def longCommSubstr(s1, s2): m = len(s1) n = len(s2)

# Create a table to store lengths of longest
# common suffixes of substrings.
dp = [[0] * (n + 1) for _ in range(m + 1)]
res = 0

# Build dp[m+1][n+1] in bottom-up fashion.
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
            res = max(res, dp[i][j])
        else:
            dp[i][j] = 0

return res

if name == "main":

s1 = "GeeksforGeeks"
s2 = "GeeksQuiz"
print(longCommSubstr(s1, s2))

C#

using System;

public class GFG {

public static int longCommSubstr(string s1, string s2) {
    int m = s1.Length;
    int n = s2.Length;

    // Create a table to store lengths of longest
    // common suffixes of substrings.
    int[,] dp = new int[m + 1, n + 1];
    int res = 0;

    // Build dp[m+1][n+1] in bottom-up fashion.
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (s1[i - 1] == s2[j - 1]) {
                dp[i, j] = dp[i - 1, j - 1] + 1;
                res = Math.Max(res, dp[i, j]);
            } else {
                dp[i, j] = 0;
            }
        }
    }

    return res;
}

public static void Main(string[] args) {
    
    string s1 = "GeeksforGeeks";
    string s2 = "GeeksQuiz";
    Console.WriteLine(longCommSubstr(s1, s2));
}

}

JavaScript

function longCommSubstr(s1, s2) { let m = s1.length; let n = s2.length;

// Create a table to store lengths of longest
// common suffixes of substrings.
let dp = Array.from(Array(m + 1), () => Array(n + 1).fill(0));
let res = 0;

// Build dp[m+1][n+1] in bottom-up fashion.
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;
            res = Math.max(res, dp[i][j]);
        } else {
            dp[i][j] = 0;
        }
    }
}

return res;

}

//Driver Code

let s1 = "GeeksforGeeks"; let s2 = "GeeksQuiz"; console.log(longCommSubstr(s1, s2));

`

[Efficient Approach] Using Space Optimized DP - O(m×n) Time and O(n) Space

From the DP state transition, we can observe that,

Since each state dp[i][j] depends only on the diagonal value from the previous row, dp[i−1][j−1], we do not need to store the entire 2D DP table. Keeping only the previous row is sufficient, which allows us to optimize the space from O(m×n) to O(n).

We keep a prev[] array for the last row and build a curr[] array for the current row.
When the characters match, we extend the substring using prev[j−1]; when they don’t, we reset the length to 0.
After completing a row, curr becomes the new prev.
This keeps the logic exactly the same while using only a linear-size array instead of the full table.

C++ `

#include #include using namespace std;

int longCommSubstr(string& s1, string& s2) { int m = s1.length(); int n = s2.length();

// Create a 1D array to store the previous row's results
vector<int> prev(n + 1, 0);

int res = 0;
for (int i = 1; i <= m; i++) {
  
    // Create a temporary array to store the current row
    vector<int> curr(n + 1, 0);
    for (int j = 1; j <= n; j++) {
        if (s1[i - 1] == s2[j - 1]) {
            curr[j] = prev[j - 1] + 1;
            res = max(res, curr[j]);
        } else {
            curr[j] = 0;
        }
    }
  
    // Move the current row's data to the previous row
    prev = curr;
}
return res;

}

int main() {

string s1 = "GeeksforGeeks";
string s2 = "GeeksQuiz";
cout << longCommSubstr(s1, s2) << endl;
return 0;

}

C

#include <stdio.h> #include <string.h>

int longCommSubstr(char* s1, char* s2) { int m = strlen(s1); int n = strlen(s2);

// Create a 1D array to store the previous row's results
int prev[n + 1];
memset(prev, 0, sizeof(prev));

int res = 0;
for (int i = 1; i <= m; i++) {
  
    // Create a temporary array to store the current row
    int curr[n + 1];
    memset(curr, 0, sizeof(curr));
    
    for (int j = 1; j <= n; j++) {
        if (s1[i - 1] == s2[j - 1]) {
            curr[j] = prev[j - 1] + 1;
            if (curr[j] > res) {
                res = curr[j];
            }
        } else {
            curr[j] = 0;
        }
    }
  
    // Move the current row's data to the previous row
    memcpy(prev, curr, sizeof(curr));
}
return res;

}

int main() {

char s1[] = "GeeksforGeeks";
char s2[] = "GeeksQuiz";
printf("%d\n", longCommSubstr(s1, s2));
return 0;

}

Java

class GfG {

public static int longCommSubstr(String s1, String s2) {
    int m = s1.length();
    int n = s2.length();

    // Create a 1D array to store the previous row's results
    int[] prev = new int[n + 1];
    
    int res = 0;
    for (int i = 1; i <= m; i++) {
      
        // Create a temporary array to store the current row
        int[] curr = new int[n + 1];
        for (int j = 1; j <= n; j++) {
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                curr[j] = prev[j - 1] + 1;
                res = Math.max(res, curr[j]);
            } else {
                curr[j] = 0;
            }
        }
      
        // Move the current row's data to the previous row
        prev = curr;
    }
    return res;
}

public static void main(String[] args) {
    
    String s1 = "GeeksforGeeks";
    String s2 = "GeeksQuiz";
    System.out.println(longCommSubstr(s1, s2));
}

}

Python

def longCommSubstr(s1, s2): m = len(s1) n = len(s2)

# Create a 1D array to store the previous row's results
prev = [0] * (n + 1)

res = 0
for i in range(1, m + 1):
  
    # Create a temporary array to store the current row
    curr = [0] * (n + 1)
    for j in range(1, n + 1):
        if s1[i - 1] == s2[j - 1]:
            curr[j] = prev[j - 1] + 1
            res = max(res, curr[j])
        else:
            curr[j] = 0
    
    # Move the current row's data to the previous row
    prev = curr

return res

if name == "main":

s1 = "GeeksforGeeks"
s2 = "GeeksQuiz"
print(longCommSubstr(s1, s2))

C#

using System;

public class GFG {

public static int longCommSubstr(string s1, string s2) {
    int m = s1.Length;
    int n = s2.Length;

    // Create a 1D array to store the previous row's results
    int[] prev = new int[n + 1];
    
    int res = 0;
    for (int i = 1; i <= m; i++) {
      
        // Create a temporary array to store the current row
        int[] curr = new int[n + 1];
        for (int j = 1; j <= n; j++) {
            if (s1[i - 1] == s2[j - 1]) {
                curr[j] = prev[j - 1] + 1;
                res = Math.Max(res, curr[j]);
            } else {
                curr[j] = 0;
            }
        }
      
        // Move the current row's data to the previous row
        prev = curr;
    }
    return res;
}

public static void Main(string[] args) {
    
    string s1 = "GeeksforGeeks";
    string s2 = "GeeksQuiz";
    Console.WriteLine(longCommSubstr(s1, s2));
}

}

JavaScript

function longCommSubstr(s1, s2) { let m = s1.length; let n = s2.length;

// Create a 1D array to store the previous row's results
let prev = new Array(n + 1).fill(0);

let res = 0;
for (let i = 1; i <= m; i++) {
  
    // Create a temporary array to store the current row
    let curr = new Array(n + 1).fill(0);
    for (let j = 1; j <= n; j++) {
        if (s1[i - 1] === s2[j - 1]) {
            curr[j] = prev[j - 1] + 1;
            res = Math.max(res, curr[j]);
        } else {
            curr[j] = 0;
        }
    }
  
    // Move the current row's data to the previous row
    prev = curr;
}
return res;

}

// Driver Code

let s1 = "GeeksforGeeks"; let s2 = "GeeksQuiz"; console.log(longCommSubstr(s1, s2));

`