Longest Common Substring in DSA C++ - Time & Space Complexity
We want to understand how the time needed to find the longest common substring between two strings changes as the strings get longer.
How does the work grow when input strings increase in size?
Analyze the time complexity of the following code snippet.
int longestCommonSubstring(const string &s1, const string &s2) {
int n = s1.size(), m = s2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
int maxLen = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (s1[i - 1] == s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
maxLen = max(maxLen, dp[i][j]);
} else {
dp[i][j] = 0;
}
}
}
return maxLen;
}
This code finds the length of the longest substring that appears in both strings by building a table of matching substring lengths.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops comparing characters of both strings and updating a table.
- How many times: Outer loop runs n times, inner loop runs m times, so total n x m times.
As the lengths of the two strings grow, the number of operations grows by multiplying their sizes.
| Input Size (n = m) | Approx. Operations |
|---|---|
| 10 | 100 |
| 100 | 10,000 |
| 1000 | 1,000,000 |
Pattern observation: Doubling the string length roughly quadruples the work because both loops multiply.
Time Complexity: O(n x m)
This means the time needed grows proportionally to the product of the lengths of the two input strings.
[X] Wrong: "The algorithm only needs to scan one string once, so it is O(n)."
[OK] Correct: The algorithm compares every character of the first string with every character of the second, so it must do work proportional to n times m.
Understanding this time complexity helps you explain how dynamic programming solves problems by breaking them into smaller parts and why nested loops often lead to multiplied work.
"What if we used a suffix tree or suffix array instead? How would the time complexity change?"
Practice
s1 and s2?Solution
Step 1: Understand the problem definition
The Longest Common Substring problem looks for the longest continuous sequence of characters that appears in both strings without any breaks.Step 2: Differentiate from similar problems
Unlike Longest Common Subsequence, which allows gaps, Longest Common Substring requires characters to be contiguous in both strings.Final Answer:
The longest sequence of characters that appear contiguously in both strings -> Option AQuick Check:
Longest Common Substring = contiguous matching characters [OK]
- Confusing substring with subsequence
- Thinking characters can be non-contiguous
- Counting total common characters instead of longest continuous
dp of size m+1 by n+1 with all zeros for Longest Common Substring?Solution
Step 1: Understand 2D vector initialization syntax
To create a 2D vector withm+1rows andn+1columns filled with zeros, use nested vector initialization.Step 2: Check each option
std::vector dp(m+1, std::vector<int>(n+1, 0)); correctly creates a vector of vectors withm+1rows, each row a vector ofn+1zeros. Others have syntax errors or wrong types.Final Answer:
std::vector dp(m+1, std::vector<int>(n+1, 0)); -> Option AQuick Check:
Correct 2D vector initialization = std::vector dp(m+1, std::vector<int>(n+1, 0)); [OK]
- Using wrong vector types in initialization
- Missing inner vector initialization
- Trying to pass three arguments to vector constructor
std::string s1 = "abcdxyz";
std::string s2 = "xyzabcd";
int m = s1.size(), n = s2.size();
std::vector dp(m+1, std::vector<int>(n+1, 0));
int maxLen = 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;
maxLen = std::max(maxLen, dp[i][j]);
}
}
}
std::cout << maxLen << std::endl;Solution
Step 1: Trace matching substrings
The longest common substring is "abcd" (length 4), which appears contiguously at the start of s1 and end of s2. "xyz" is length 3.Step 2: Check dp updates and maxLen
dp updates length for matching characters. The longest continuous match found is length 4 for "abcd".Final Answer:
4 -> Option BQuick Check:
Longest common substring length = 4 [OK]
- Confusing substring with subsequence length
- Counting non-contiguous matches
- Off-by-one errors in indexing
std::string s1 = "abcde";
std::string s2 = "abfde";
int m = s1.size(), n = s2.size();
std::vector dp(m+1, std::vector<int>(n+1, 0));
int maxLen = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s1[i] == s2[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
maxLen = std::max(maxLen, dp[i][j]);
}
}
}
std::cout << maxLen << std::endl;Solution
Step 1: Check string indexing inside loops
Loop variables i and j start from 1, so to access string characters correctly, indices should be i-1 and j-1.Step 2: Identify the incorrect comparison
The code compares s1[i] and s2[j], which causes out-of-bound access and wrong comparisons. It should be s1[i-1] == s2[j-1].Final Answer:
The comparison should be s1[i-1] == s2[j-1] instead of s1[i] == s2[j] -> Option DQuick Check:
Correct string indexing = i-1 and j-1 [OK]
- Accessing string with i or j directly causing out-of-bounds
- Not initializing dp with correct size
- Starting loops at wrong indices
s1 = "banana" and s2 = "ananas", write a C++ function to find the longest common substring and return it. What is the correct longest common substring?Solution
Step 1: Understand the longest common substring candidates
Check substrings common to both strings. "ana" (length 3) appears multiple times in both strings. "anana" is not a substring of both strings contiguously.Step 2: Verify other options
"nana" length 4 is not a substring of s2; "ban" only in s1; "anana" is not a substring of s2.Final Answer:
"ana" -> Option CQuick Check:
Longest common substring between "banana" and "ananas" is "ana" [OK]
- Choosing longest common subsequence instead of substring
- Ignoring continuity requirement
- Assuming longer substrings without verifying presence
Ready to prove it?
Pass the test and this pattern moves unknown
