Given two strings s1 and s2, Find the length of the shortest string that has both s1 and s2 as subsequences.
A subsequence of a string is a sequence that can be derived from the string by deleting zero or more characters without changing the relative order of the remaining characters.
Examples:
Input: s1 = "geek", s2 = "eke"
Output: 5
Explanation: String "geeke" has both string "geek" and "eke" as subsequences.Input: s1 = "AGGTAB", s2 = "GXTXAYB"
Output: 9
Explanation: String "AGXGTXAYB" has both string "AGGTAB" and "GXTXAYB" as subsequences.
Table of Content
- [Naive Approach] Using Recursion - O(2^(m+n)) Time and O(m+n) Auxiliary Space
- [Better Approach - 1] Using Top-Down DP (Memoization) - O(m*n) Time and O(m*n) Space
- [Better Approach - 2] Using Bottom-Up DP (Tabulation) - O(m*n) Time and O(m*n) Space
- [Expected Approach - 1] Using Space Optimized DP - O(m*n) Time and O(n) Space
- [Expected Appraoch - 2] Using LCS Solution - O(m*n) Time and O(n) Space
[Naive Approach] Using Recursion - O(2^(m+n)) Time and O(m+n) Auxiliary Space
Our goal is to build the shortest string that contains both s1 and s2 as subsequences. A naive thought might be to simply merge all characters of both strings - but that actually gives the longest, not the shortest, supersequence because common characters get counted twice.
The key insight is: If a character appears in both strings and in the same order, we can include it only once in our supersequence.
However, when characters differ, we have choices — and recursion is perfect for exploring such choice-based problems.
We start comparing the characters of both strings from the end. If the last characters match: That character will definitely appear once in the supersequence. We include it and move one step back in both strings.
If the last characters don’t match: We have two options:
- Include the last character of s1 and keep s2 as it is.
- Include the last character of s2 and keep s1 as it is.
We choose the option that gives the shorter result. When one string becomes empty, it means there’s nothing left to match. So, we simply add all remaining characters of the other string to complete the sequence.
//Driver Code Starts
#include <iostream>
#include<vector>
using namespace std;
//Driver Code Ends
int minSuperSeqRec(string &s1, string &s2, int m, int n) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// If last chars match, include once
if (s1[m - 1] == s2[n - 1])
return 1 + minSuperSeqRec(s1, s2, m - 1, n - 1);
// If not match, try both options and take min
return 1 + min(minSuperSeqRec(s1, s2, m - 1, n),
minSuperSeqRec(s1, s2, m, n - 1));
}
int minSuperSeq(string &s1, string &s2) {
return minSuperSeqRec(s1, s2, s1.size(), s2.size());
}
//Driver Code Starts
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
cout << res << endl;
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.Arrays;
class GFG {
//Driver Code Ends
static int minSuperSeqRec(String s1, String s2, int m, int n) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// If last chars match, include once
if (s1.charAt(m - 1) == s2.charAt(n - 1))
return 1 + minSuperSeqRec(s1, s2, m - 1, n - 1);
// If not match, try both options and take min
return 1 + Math.min(minSuperSeqRec(s1, s2, m - 1, n),
minSuperSeqRec(s1, s2, m, n - 1));
}
static int minSuperSeq(String s1, String s2) {
return minSuperSeqRec(s1, s2, s1.length(), s2.length());
}
//Driver Code Starts
public static void main(String[] args) {
String s1 = "AGGTAB";
String s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
System.out.println(res);
}
}
//Driver Code Ends
def minSuperSeqRec(s1, s2, m, n):
# If s1 is empty, take all of s2
if m == 0:
return n
# If s2 is empty, take all of s1
if n == 0:
return m
# If last chars match, include once
if s1[m - 1] == s2[n - 1]:
return 1 + minSuperSeqRec(s1, s2, m - 1, n - 1)
# If not match, try both options and take min
return 1 + min(minSuperSeqRec(s1, s2, m - 1, n),
minSuperSeqRec(s1, s2, m, n - 1))
def minSuperSeq(s1, s2):
return minSuperSeqRec(s1, s2, len(s1), len(s2))
#Driver Code Starts
if __name__ == "__main__":
s1 = "AGGTAB"
s2 = "GXTXAYB"
res = minSuperSeq(s1, s2)
print(res)
#Driver Code Ends
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
static int minSuperSeqRec(string s1, string s2, int m, int n) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// If last chars match, include once
if (s1[m - 1] == s2[n - 1])
return 1 + minSuperSeqRec(s1, s2, m - 1, n - 1);
// If not match, try both options and take min
return 1 + Math.Min(minSuperSeqRec(s1, s2, m - 1, n),
minSuperSeqRec(s1, s2, m, n - 1));
}
static int minSuperSeq(string s1, string s2) {
return minSuperSeqRec(s1, s2, s1.Length, s2.Length);
}
//Driver Code Starts
static void Main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
Console.WriteLine(res);
}
}
//Driver Code Ends
function minSuperSeqRec(s1, s2, m, n) {
// If s1 is empty, take all of s2
if (m === 0)
return n;
// If s2 is empty, take all of s1
if (n === 0)
return m;
// If last chars match, include once
if (s1[m - 1] === s2[n - 1])
return 1 + minSuperSeqRec(s1, s2, m - 1, n - 1);
// If not match, try both options and take min
return 1 + Math.min(minSuperSeqRec(s1, s2, m - 1, n),
minSuperSeqRec(s1, s2, m, n - 1));
}
function minSuperSeq(s1, s2) {
return minSuperSeqRec(s1, s2, s1.length, s2.length);
}
//Driver Code
//Driver Code Starts
let s1 = "AGGTAB";
let s2 = "GXTXAYB";
let res = minSuperSeq(s1, s2);
console.log(res);
//Driver Code Ends
Output
9
[Better Approach - 1] Using Top-Down DP (Memoization) - O(m*n) Time and O(m*n) Space
If we look closely at the previous recursive approach, we can notice the same subproblems are solved multiple times. This makes a program inefficient.
To handle this, we use Dynamic Programming with Memoization.
The idea is simple: In the recursive approach, our result depends on two parameters - the current indices m and n. So, we create a 2D array dp of size (m+1) × (n+1), where dp[i][j] stores the length of the shortest common supersequence for the first i characters of s1 and the first j characters of s2.
Before solving any subproblem (i, j), we first check:
If it has been solved before, we simply return the stored value. Otherwise, we compute it recursively, store the result in dp[i][j], and reuse it later whenever needed.
//Driver Code Starts
#include <iostream>
#include<vector>
using namespace std;
//Driver Code Ends
int minSuperSeqRec(string &s1, string &s2, int m, int n,
vector<vector<int>> &dp) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// check before solve the subproblem
if (dp[m][n] != -1)
return dp[m][n];
// If last chars match, include once
if (s1[m - 1] == s2[n - 1])
return dp[m][n] = 1 + minSuperSeqRec(s1, s2, m - 1, n - 1, dp);
// If not match, try both options and take min
// and store it in dp array
return dp[m][n] =
1 + min(minSuperSeqRec(s1, s2, m - 1, n, dp),
minSuperSeqRec(s1, s2, m, n - 1, dp));
}
int minSuperSeq(string &s1, string &s2) {
int m = s1.size();
int n = s2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, -1));
return minSuperSeqRec(s1, s2, m, n, dp);
}
//Driver Code Starts
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
cout << res << endl;
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.Arrays;
public class GFG {
//Driver Code Ends
static int minSuperSeqRec(String s1, String s2, int m, int n,
int[][] dp) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// check before solve the subproblem
if (dp[m][n] != -1)
return dp[m][n];
// If last chars match, include once
if (s1.charAt(m - 1) == s2.charAt(n - 1))
return dp[m][n] = 1 + minSuperSeqRec(s1, s2, m - 1, n - 1, dp);
// If not match, try both options and take min
// and store it in dp array
return dp[m][n] =
1 + Math.min(minSuperSeqRec(s1, s2, m - 1, n, dp),
minSuperSeqRec(s1, s2, m, n - 1, dp));
}
static int minSuperSeq(String s1, String s2) {
int m = s1.length();
int n = s2.length();
int[][] dp = new int[m + 1][n + 1];
for (int[] row : dp) Arrays.fill(row, -1);
return minSuperSeqRec(s1, s2, m, n, dp);
}
//Driver Code Starts
public static void main(String[] args) {
String s1 = "AGGTAB";
String s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
System.out.println(res);
}
}
//Driver Code Ends
def minSuperSeqRec(s1, s2, m, n, dp):
# If s1 is empty, take all of s2
if m == 0:
return n
# If s2 is empty, take all of s1
if n == 0:
return m
# check before solve the subproblem
if dp[m][n] != -1:
return dp[m][n]
# If last chars match, include once
if s1[m - 1] == s2[n - 1]:
dp[m][n] = 1 + minSuperSeqRec(s1, s2, m - 1, n - 1, dp)
return dp[m][n]
# If not match, try both options and take min
# and store it in dp array
dp[m][n] = 1 + min(minSuperSeqRec(s1, s2, m - 1, n, dp),
minSuperSeqRec(s1, s2, m, n - 1, dp))
return dp[m][n]
def minSuperSeq(s1, s2):
m = len(s1)
n = len(s2)
dp = [[-1] * (n + 1) for _ in range(m + 1)]
return minSuperSeqRec(s1, s2, m, n, dp)
#Driver Code Starts
if __name__ == "__main__":
s1 = "AGGTAB"
s2 = "GXTXAYB"
res = minSuperSeq(s1, s2)
print(res)
#Driver Code Ends
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
static int minSuperSeqRec(string s1, string s2, int m, int n, int[,] dp) {
// If s1 is empty, take all of s2
if (m == 0)
return n;
// If s2 is empty, take all of s1
if (n == 0)
return m;
// check before solve the subproblem
if (dp[m, n] != -1)
return dp[m, n];
// If last chars match, include once
if (s1[m - 1] == s2[n - 1])
return dp[m, n] = 1 + minSuperSeqRec(s1, s2, m - 1, n - 1, dp);
// If not match, try both options and take min
// and store it in dp array
return dp[m, n] =
1 + Math.Min(minSuperSeqRec(s1, s2, m - 1, n, dp),
minSuperSeqRec(s1, s2, m, n - 1, dp));
}
static int minSuperSeq(string s1, string s2) {
int m = s1.Length;
int n = s2.Length;
int[,] dp = new int[m + 1, n + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++)
dp[i, j] = -1;
}
return minSuperSeqRec(s1, s2, m, n, dp);
}
//Driver Code Starts
static void Main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
Console.WriteLine(res);
}
}
//Driver Code Ends
function minSuperSeqRec(s1, s2, m, n, dp) {
// If s1 is empty, take all of s2
if (m === 0)
return n;
// If s2 is empty, take all of s1
if (n === 0)
return m;
// check before solve the subproblem
if (dp[m][n] !== -1)
return dp[m][n];
// If last chars match, include once
if (s1[m - 1] === s2[n - 1])
return dp[m][n] = 1 + minSuperSeqRec(s1, s2, m - 1, n - 1, dp);
// If not match, try both options and take min
// and store it in dp array
return dp[m][n] =
1 + Math.min(minSuperSeqRec(s1, s2, m - 1, n, dp),
minSuperSeqRec(s1, s2, m, n - 1, dp));
}
function minSuperSeq(s1, s2) {
const m = s1.length;
const n = s2.length;
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(-1));
return minSuperSeqRec(s1, s2, m, n, dp);
}
//Driver Code
//Driver Code Starts
const s1 = "AGGTAB";
const s2 = "GXTXAYB";
const res = minSuperSeq(s1, s2);
console.log(res);
//Driver Code Ends
Output
9
[Better Approach - 2] Using Bottom-Up DP (Tabulation) - O(m*n) Time and O(m*n) Space
In the recursive and memoized methods, we broke the problem into smaller subproblems using function calls - which also involved extra stack space. In this approach, we will iteratively compute and store results for all smaller subproblems in a 2D DP table and then use these results to build up the final answer.
We create a 2D DP array dp of size (m + 1) x (n + 1), where m and n are the lengths of s1 and s2. First, we define some base cases to start the iteration: dp[i][0] = i and dp[0][j] = j.
This means if one string is empty, the shortest supersequence is simply the other string. Next, we iterate over both strings character by character:
- If the characters match, we include this character only once: dp[i][j] = 1 + dp[i-1][j-1]
- If the characters don’t match, we have two choices - take a character from s1 or from s2 - and we choose the one that gives the smaller length: dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1])
After filling the entire table, dp[m][n] will hold the length of the Shortest Common Supersequence (SCS) of s1 and s2.
//Driver Code Starts
#include <iostream>
#include<vector>
using namespace std;
//Driver Code Ends
int minSuperSeq(string &s1, string &s2) {
int m = s1.size();
int n = s2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
// If s2 is empty, take all of s1
for (int i = 0; i <= m; i++)
dp[i][0] = i;
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
dp[0][j] = j;
// Fill the dp table iteratively
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] == s2[j - 1])
dp[i][j] = 1 + dp[i - 1][j - 1];
// If not match, take min of both options
else
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
//Driver Code Starts
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
cout << res << endl;
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.Arrays;
public class GFG {
//Driver Code Ends
static int minSuperSeq(String s1, String s2) {
int m = s1.length();
int n = s2.length();
int[][] dp = new int[m + 1][n + 1];
// If s2 is empty, take all of s1
for (int i = 0; i <= m; i++)
dp[i][0] = i;
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
dp[0][j] = j;
// Fill the dp table iteratively
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1.charAt(i - 1) == s2.charAt(j - 1))
dp[i][j] = 1 + dp[i - 1][j - 1];
// If not match, take min of both options
else
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
//Driver Code Starts
public static void main(String[] args) {
String s1 = "AGGTAB";
String s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
System.out.println(res);
}
}
//Driver Code Ends
def minSuperSeq(s1, s2):
m = len(s1)
n = len(s2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
# If s2 is empty, take all of s1
for i in range(m + 1):
dp[i][0] = i
# If s1 is empty, take all of s2
for j in range(n + 1):
dp[0][j] = j
# Fill the dp table iteratively
for i in range(1, m + 1):
for j in range(1, n + 1):
# If last chars match, include once
if s1[i - 1] == s2[j - 1]:
dp[i][j] = 1 + dp[i - 1][j - 1]
# If not match, take min of both options
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1])
return dp[m][n]
#Driver Code Starts
if __name__ == "__main__":
s1 = "AGGTAB"
s2 = "GXTXAYB"
res = minSuperSeq(s1, s2)
print(res)
#Driver Code Ends
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
static int minSuperSeq(string s1, string s2) {
int m = s1.Length;
int n = s2.Length;
int[,] dp = new int[m + 1, n + 1];
// If s2 is empty, take all of s1
for (int i = 0; i <= m; i++)
dp[i, 0] = i;
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
dp[0, j] = j;
// Fill the dp table iteratively
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] == s2[j - 1])
dp[i, j] = 1 + dp[i - 1, j - 1];
// If not match, take min of both options
else
dp[i, j] = 1 + Math.Min(dp[i - 1, j], dp[i, j - 1]);
}
}
return dp[m, n];
}
//Driver Code Starts
static void Main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
Console.WriteLine(res);
}
}
//Driver Code Ends
function minSuperSeq(s1, s2) {
const m = s1.length;
const n = s2.length;
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
// If s2 is empty, take all of s1
for (let i = 0; i <= m; i++)
dp[i][0] = i;
// If s1 is empty, take all of s2
for (let j = 0; j <= n; j++)
dp[0][j] = j;
// Fill the dp table iteratively
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] === s2[j - 1])
dp[i][j] = 1 + dp[i - 1][j - 1];
// If not match, take min of both options
else
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
//Driver Code
//Driver Code Starts
const s1 = "AGGTAB";
const s2 = "GXTXAYB";
const res = minSuperSeq(s1, s2);
console.log(res);
//Driver Code Ends
Output
9
[Expected Approach - 1] Using Space Optimized DP - O(m*n) Time and O(n) Space
In the previous dynamic programming approach, we derived the relation between states as:
if (s1[i-1] == s2[j-1]) dp[i][j] = 1 + dp[i-1][j-1] that means both characters are the same, so we take it once.
else dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1]) we take the minimum of both possibilities.
If we observe carefully, to calculate the current cell dp[i][j], we only need values from: the previous row - dp[i-1][j-1] and dp[i-1][j], and the current row -> dp[i][j-1].
This means we don’t need to store the entire 2D table of size (m + 1) x (n + 1).
We can optimize the space by using only two 1D arrays - one for the current row and one for the previous row. So instead of maintaining a complete DP matrix, we keep a prev array to store the previous row. Create a curr array for the current row calculations. After finishing one iteration of
i, assignprev = currfor the next iteration.
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends
int minSuperSeq(string &s1, string &s2) {
int m = s1.size();
int n = s2.size();
// Use two 1D arrays to store previous and current row
vector<int> prev(n + 1, 0), curr(n + 1, 0);
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
prev[j] = j;
// Iterate through both strings
for (int i = 1; i <= m; i++) {
// If s2 is empty, take all of s1
curr[0] = i;
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] == s2[j - 1])
curr[j] = 1 + prev[j - 1];
// If not match, take min of both options
else
curr[j] = 1 + min(prev[j], curr[j - 1]);
}
// Move current row to previous for next iteration
prev = curr;
}
return prev[n];
}
//Driver Code Starts
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
cout << res << endl;
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.Arrays;
public class GFG {
//Driver Code Ends
static int minSuperSeq(String s1, String s2) {
int m = s1.length();
int n = s2.length();
// Use two 1D arrays to store previous and current row
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
prev[j] = j;
// Iterate through both strings
for (int i = 1; i <= m; i++) {
// If s2 is empty, take all of s1
curr[0] = i;
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1.charAt(i - 1) == s2.charAt(j - 1))
curr[j] = 1 + prev[j - 1];
// If not match, take min of both options
else
curr[j] = 1 + Math.min(prev[j], curr[j - 1]);
}
// Move current row to previous for next iteration
prev = curr.clone();
}
return prev[n];
}
//Driver Code Starts
public static void main(String[] args) {
String s1 = "AGGTAB";
String s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
System.out.println(res);
}
}
//Driver Code Ends
def minSuperSeq(s1, s2):
m = len(s1)
n = len(s2)
# Use two 1D arrays to store previous and current row
prev = [0] * (n + 1)
curr = [0] * (n + 1)
# If s1 is empty, take all of s2
for j in range(n + 1):
prev[j] = j
# Iterate through both strings
for i in range(1, m + 1):
# If s2 is empty, take all of s1
curr[0] = i
for j in range(1, n + 1):
# If last chars match, include once
if s1[i - 1] == s2[j - 1]:
curr[j] = 1 + prev[j - 1]
# If not match, take min of both options
else:
curr[j] = 1 + min(prev[j], curr[j - 1])
# Move current row to previous for next iteration
prev = curr[:]
return prev[n]
#Driver Code Starts
if __name__ == "__main__":
s1 = "AGGTAB"
s2 = "GXTXAYB"
res = minSuperSeq(s1, s2)
print(res)
#Driver Code Ends
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
static int minSuperSeq(string s1, string s2) {
int m = s1.Length;
int n = s2.Length;
// Use two 1D arrays to store previous and current row
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
// If s1 is empty, take all of s2
for (int j = 0; j <= n; j++)
prev[j] = j;
// Iterate through both strings
for (int i = 1; i <= m; i++) {
// If s2 is empty, take all of s1
curr[0] = i;
for (int j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] == s2[j - 1])
curr[j] = 1 + prev[j - 1];
// If not match, take min of both options
else
curr[j] = 1 + Math.Min(prev[j], curr[j - 1]);
}
// Move current row to previous for next iteration
Array.Copy(curr, prev, n + 1);
}
return prev[n];
}
//Driver Code Starts
static void Main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
Console.WriteLine(res);
}
}
//Driver Code Ends
function minSuperSeq(s1, s2) {
const m = s1.length;
const n = s2.length;
// Use two 1D arrays to store previous and current row
let prev = Array(n + 1).fill(0);
let curr = Array(n + 1).fill(0);
// If s1 is empty, take all of s2
for (let j = 0; j <= n; j++)
prev[j] = j;
// Iterate through both strings
for (let i = 1; i <= m; i++) {
// If s2 is empty, take all of s1
curr[0] = i;
for (let j = 1; j <= n; j++) {
// If last chars match, include once
if (s1[i - 1] === s2[j - 1])
curr[j] = 1 + prev[j - 1];
// If not match, take min of both options
else
curr[j] = 1 + Math.min(prev[j], curr[j - 1]);
}
// Move current row to previous for next iteration
prev = [...curr];
}
return prev[n];
}
//Driver Code
//Driver Code Starts
const s1 = "AGGTAB";
const s2 = "GXTXAYB";
const res = minSuperSeq(s1, s2);
console.log(res);
//Driver Code Ends
Output
9
[Expected Approach - 2] Using LCS Solution - O(m*n) Time and O(n) Space
If we look carefully, we are asked to find the Shortest Common Supersequence (SCS). Now, if both strings had no common characters, then we would need to take all characters from both strings. So, the length of SCS = len(s1) + len(s2). But when the strings share common characters, those common parts should appear only once in the final supersequence to minimize length. That’s where LCS (Longest Common Subsequence) helps us.
We know the relation between SCS and LCS: Length(SCS) = len(s1) + len(s2) − len(LCS)
So instead of directly computing SCS, we just need to find LCS length.
In LCS DP, to compute dp[i][j], we only need:
- dp[i-1][j-1] - from previous row
- dp[i][j-1] - from current row
- dp[i-1][j] - from previous row
Hence, we can use only two 1D arrays - prev and curr.
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends
int minSuperSeq(string &s1, string &s2) {
int m = s1.size();
int n = s2.size();
vector<int> prev(n + 1, 0), curr(n + 1, 0);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If chars match
if (s1[i - 1] == s2[j - 1])
curr[j] = 1 + prev[j - 1];
// Else, take max from top or left
else
curr[j] = max(prev[j], curr[j - 1]);
}
// Move current row to previous
prev = curr;
}
int lcsLen = prev[n];
// SCS length = total - common part (LCS)
return m + n - lcsLen;
}
//Driver Code Starts
int main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
cout << res << endl;
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.Arrays;
public class GFG {
//Driver Code Ends
static int minSuperSeq(String s1, String s2) {
int m = s1.length();
int n = s2.length();
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If chars match
if (s1.charAt(i - 1) == s2.charAt(j - 1))
curr[j] = 1 + prev[j - 1];
// Else, take max from top or left
else
curr[j] = Math.max(prev[j], curr[j - 1]);
}
// Move current row to previous
prev = curr.clone();
}
int lcsLen = prev[n];
// SCS length = total - common part (LCS)
return m + n - lcsLen;
}
//Driver Code Starts
public static void main(String[] args) {
String s1 = "AGGTAB";
String s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
System.out.println(res);
}
}
//Driver Code Ends
def minSuperSeq(s1, s2):
m, n = len(s1), len(s2)
prev = [0] * (n + 1)
curr = [0] * (n + 1)
for i in range(1, m + 1):
for j in range(1, n + 1):
# If chars match
if s1[i - 1] == s2[j - 1]:
curr[j] = 1 + prev[j - 1]
# Else, take max from top or left
else:
curr[j] = max(prev[j], curr[j - 1])
# Move current row to previous
prev = curr[:]
lcsLen = prev[n]
# SCS length = total - common part (LCS)
return m + n - lcsLen
if __name__ == "__main__":
#Driver Code Starts
s1 = "AGGTAB"
s2 = "GXTXAYB"
res = minSuperSeq(s1, s2)
print(res)
#Driver Code Ends
//Driver Code Starts
using System;
class GFG {
//Driver Code Ends
static int minSuperSeq(string s1, string s2) {
int m = s1.Length;
int n = s2.Length;
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// If chars match
if (s1[i - 1] == s2[j - 1])
curr[j] = 1 + prev[j - 1];
// Else, take max from top or left
else
curr[j] = Math.Max(prev[j], curr[j - 1]);
}
// Move current row to previous
Array.Copy(curr, prev, n + 1);
}
int lcsLen = prev[n];
// SCS length = total - common part (LCS)
return m + n - lcsLen;
}
//Driver Code Starts
static void Main() {
string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = minSuperSeq(s1, s2);
Console.WriteLine(res);
}
}
//Driver Code Ends
function minSuperSeq(s1, s2) {
const m = s1.length;
const n = s2.length;
let prev = new Array(n + 1).fill(0);
let curr = new Array(n + 1).fill(0);
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// If chars match
if (s1[i - 1] === s2[j - 1])
curr[j] = 1 + prev[j - 1];
// Else, take max from top or left
else
curr[j] = Math.max(prev[j], curr[j - 1]);
}
// Move current row to previous
prev = [...curr];
}
const lcsLen = prev[n];
// SCS length = total - common part (LCS)
return m + n - lcsLen;
}
//Driver Code
//Driver Code Starts
const s1 = "AGGTAB";
const s2 = "GXTXAYB";
const res = minSuperSeq(s1, s2);
console.log(res);
//Driver Code Ends
Output
9