Given an array a[] and another array b[]. Find the minimum number of elements to be added in b[] so that a[] becomes subsequence of b[]. Note that you can add elements at any position in b[].
Examples:
Input: a[] = [1, 2, 3, 4, 5], b[] = [2, 5, 6, 4, 9, 12]
Output: 3
Explanation: Insert 1 before 2, 3 between 2 and 5, and 4 before 5. One possible modified array is [1, 2, 3, 4, 5, 6, 4, 9, 12]. Now a[] is a subsequence of b[]. Hence, the minimum number of insertions required is 3.Input: a[] = [1], b[] = [1]
Output: 0
Explanation: a[] is already a subsequence of b[], so no insertions are required.
Table of Content
[Naive Approach] Try All Insertions - O(2 ^ (n + m)) Time and O(n + m) Space
The idea is to recursively try all possibilities. Whenever the current elements do not match, either insert the current element of a into b or skip the current element of b, and return the minimum insertions required.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int minInsertions(int i, int j, vector<int> &a, vector<int> &b)
{
// All elements of a are matched
if (i == a.size())
return 0;
// No elements left in b, insert remaining elements of a
if (j == b.size())
return a.size() - i;
// Current elements match
if (a[i] == b[j])
return minInsertions(i + 1, j + 1, a, b);
// Either insert a[i] into b or skip current element of b
return min(1 + minInsertions(i + 1, j, a, b), minInsertions(i, j + 1, a, b));
}
int makeSubsequences(vector<int> &a, vector<int> &b)
{
return minInsertions(0, 0, a, b);
}
int main()
{
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b = {2, 5, 6, 4, 9, 12};
cout << makeSubsequences(a, b);
return 0;
}
import java.util.Arrays;
public class GFG {
public static int minInsertions(int i, int j, int[] a,
int[] b)
{
// All elements of a are matched
if (i == a.length)
return 0;
// No elements left in b, insert remaining elements
// of a
if (j == b.length)
return a.length - i;
// Current elements match
if (a[i] == b[j])
return minInsertions(i + 1, j + 1, a, b);
// Either insert a[i] into b or skip current element
// of b
return Math.min(1 + minInsertions(i + 1, j, a, b),
minInsertions(i, j + 1, a, b));
}
public static int makeSubsequences(int[] a, int[] b)
{
return minInsertions(0, 0, a, b);
}
public static void main(String[] args)
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
System.out.println(makeSubsequences(a, b));
}
}
def minInsertions(i, j, a, b):
# All elements of a are matched
if i == len(a):
return 0
# No elements left in b, insert remaining elements of a
if j == len(b):
return len(a) - i
# Current elements match
if a[i] == b[j]:
return minInsertions(i + 1, j + 1, a, b)
# Either insert a[i] into b or skip current element of b
return min(1 + minInsertions(i + 1, j, a, b), minInsertions(i, j + 1, a, b))
def makeSubsequences(a, b):
return minInsertions(0, 0, a, b)
if __name__ == '__main__':
a = [1, 2, 3, 4, 5]
b = [2, 5, 6, 4, 9, 12]
print(makeSubsequences(a, b))
using System;
public class GFG {
public static int minInsertions(int i, int j, int[] a,
int[] b)
{
// All elements of a are matched
if (i == a.Length)
return 0;
// No elements left in b, insert remaining elements
// of a
if (j == b.Length)
return a.Length - i;
// Current elements match
if (a[i] == b[j])
return minInsertions(i + 1, j + 1, a, b);
// Either insert a[i] into b or skip current element
// of b
return Math.Min(1 + minInsertions(i + 1, j, a, b),
minInsertions(i, j + 1, a, b));
}
public static int makeSubsequences(int[] a, int[] b)
{
return minInsertions(0, 0, a, b);
}
public static void Main()
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
Console.WriteLine(makeSubsequences(a, b));
}
}
function minInsertions(i, j, a, b)
{
// All elements of a are matched
if (i === a.length)
return 0;
// No elements left in b, insert remaining elements of a
if (j === b.length)
return a.length - i;
// Current elements match
if (a[i] === b[j])
return minInsertions(i + 1, j + 1, a, b);
// Either insert a[i] into b or skip current element of
// b
return Math.min(1 + minInsertions(i + 1, j, a, b),
minInsertions(i, j + 1, a, b));
}
function makeSubsequences(a, b)
{
return minInsertions(0, 0, a, b);
}
// Driver Code
const a = [ 1, 2, 3, 4, 5 ];
const b = [ 2, 5, 6, 4, 9, 12 ];
console.log(makeSubsequences(a, b));
Output
3
[Better Approach] Using LCS (Bottom-Up Tabulation) - O(n * m) Time and O(n * m) Space
The idea is to build the LCS table iteratively. Each cell stores the LCS length for prefixes of a and b. The answer is obtained by subtracting the LCS length from the size of a.
Working of Approach:
- Create a DP table where dp[i][j] stores the LCS length for the first i elements of a and the first j elements of b.
- Fill the table iteratively from smaller prefixes to larger prefixes.
- If the current elements match, extend the LCS by one; otherwise, take the maximum of the adjacent states.
- The last cell of the table contains the length of the LCS of the two arrays.
- The minimum insertions required are a.size() - dp[n][m].
Let us understand with an example:
Input: a[] = [1, 2, 3, 4, 5], b[] = [2, 5, 6, 4, 9, 12]
- Create a DP table where dp[i][j] stores the LCS length between the first i elements of a and the first j elements of b.
- Traverse both arrays and fill the table by matching equal elements or taking the maximum of the previous states.
- For the given input, the computed LCS is [2, 5], so dp[5][6] = 2.
- This means 2 elements of a are already present in b in the correct order.
- Hence, the minimum insertions required are 5 - 2 = 3.
#include <iostream>
#include <vector>
using namespace std;
int makeSubsequences(vector<int> &a, vector<int> &b)
{
int n = a.size();
int m = b.size();
// dp[i][j] stores the length of the LCS between
// the first i elements of a and the first j elements of b
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// Build the LCS table
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
// Current elements match
if (a[i - 1] == b[j - 1])
dp[i][j] = 1 + dp[i - 1][j - 1];
// Current elements do not match
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
// Remaining elements of a must be inserted
return n - dp[n][m];
}
int main()
{
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b = {2, 5, 6, 4, 9, 12};
cout << makeSubsequences(a, b);
return 0;
}
import java.util.*;
public class GFG {
static int makeSubsequences(int[] a, int[] b)
{
int n = a.length;
int m = b.length;
// dp[i][j] stores the length of the LCS between
// the first i elements of a and the first j
// elements of b
int[][] dp = new int[n + 1][m + 1];
// Build the LCS table
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// Current elements match
if (a[i - 1] == b[j - 1])
dp[i][j] = 1 + dp[i - 1][j - 1];
// Current elements do not match
else
dp[i][j] = Math.max(dp[i - 1][j],
dp[i][j - 1]);
}
}
// Remaining elements of a must be inserted
return n - dp[n][m];
}
public static void main(String[] args)
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
System.out.println(makeSubsequences(a, b));
}
}
from typing import List
def makeSubsequences(a: List[int], b: List[int]) -> int:
n = len(a)
m = len(b)
# dp[i][j] stores the length of the LCS between
# the first i elements of a and the first j elements of b
dp = [[0] * (m + 1) for _ in range(n + 1)]
# Build the LCS table
for i in range(1, n + 1):
for j in range(1, m + 1):
# Current elements match
if a[i - 1] == b[j - 1]:
dp[i][j] = 1 + dp[i - 1][j - 1]
# Current elements do not match
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
# Remaining elements of a must be inserted
return n - dp[n][m]
if __name__ == '__main__':
a = [1, 2, 3, 4, 5]
b = [2, 5, 6, 4, 9, 12]
print(makeSubsequences(a, b))
using System;
public class GFG {
static int makeSubsequences(int[] a, int[] b)
{
int n = a.Length;
int m = b.Length;
// dp[i][j] stores the length of the LCS between
// the first i elements of a and the first j
// elements of b
int[, ] dp = new int[n + 1, m + 1];
// Build the LCS table
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// Current elements match
if (a[i - 1] == b[j - 1])
dp[i, j] = 1 + dp[i - 1, j - 1];
// Current elements do not match
else
dp[i, j] = Math.Max(dp[i - 1, j],
dp[i, j - 1]);
}
}
// Remaining elements of a must be inserted
return n - dp[n, m];
}
public static void Main()
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
Console.WriteLine(makeSubsequences(a, b));
}
}
function makeSubsequences(a, b)
{
let n = a.length;
let m = b.length;
// dp[i][j] stores the length of the LCS between
// the first i elements of a and the first j elements of
// b
let dp = Array.from({length : n + 1},
() => Array(m + 1).fill(0));
// Build the LCS table
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
// Current elements match
if (a[i - 1] === b[j - 1]) {
dp[i][j] = 1 + dp[i - 1][j - 1];
}
// Current elements do not match
else {
dp[i][j]
= Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// Remaining elements of a must be inserted
return n - dp[n][m];
}
// Driver Code
let a = [ 1, 2, 3, 4, 5 ];
let b = [ 2, 5, 6, 4, 9, 12 ];
console.log(makeSubsequences(a, b));
Output
3
[Expected Approach] Using LCS (Space Optimized DP) - O(n * m) Time and O(m) Space
The idea is to compute the Longest Common Subsequence (LCS) between a and b using a space-optimized DP table. The LCS gives the maximum number of elements already present in the correct order, so the remaining elements of a must be inserted into b.
Working of Approach:
- Create two DP rows to store the LCS values for the current and previous rows.
- Traverse both arrays and update the current row based on whether the current elements match.
- If the elements match, extend the LCS by one; otherwise, take the maximum of the left and upper values.
- Reuse the two rows for every iteration to reduce the auxiliary space from O(n × m) to O(m).
- The minimum insertions required are a.size() - length of the LCS.
Let us understand with an example:
Input: a[] = [1, 2, 3, 4, 5], b[] = [2, 5, 6, 4, 9, 12]
- Create two DP rows to store the LCS values for the previous and current iterations.
- Traverse both arrays and update the current row. Matching elements increase the LCS length, while non-matching elements take the maximum of the left and upper values.
- After processing all elements, the last computed LCS length is 2, corresponding to the subsequence [2, 5].
- Thus, 2 elements of a already appear in b in the correct order, and the remaining 3 elements must be inserted.
- Therefore, the minimum insertions required are 5 - 2 = 3.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// Returns length of LCS
int lcs(vector<int> &a, vector<int> &b)
{
int n = a.size();
int m = b.size();
vector<vector<int>> dp(2, vector<int>(m + 1, 0));
// Binary index, used to
// index current row and
// previous row.
bool bi;
for (int i = 0; i <= n; i++)
{
// Compute current
// binary index
bi = i & 1;
for (int j = 0; j <= m; j++)
{
if (i == 0 || j == 0)
dp[bi][j] = 0;
else if (a[i - 1] == b[j - 1])
dp[bi][j] = dp[1 - bi][j - 1] + 1;
else
dp[bi][j] = max(dp[1 - bi][j], dp[bi][j - 1]);
}
}
// Last filled entry contains
// length of LCS
// for a[0..n-1] and b[0..m-1]
return dp[bi][m];
}
int makeSubsequences(vector<int> &a, vector<int> &b)
{
// Required answer is length of Array a minus
// length of longest common subsequence of a & b.
int ans = a.size() - lcs(a, b);
return ans;
}
int main()
{
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b = {2, 5, 6, 4, 9, 12};
cout << makeSubsequences(a, b);
return 0;
}
public class GFG {
// Returns length of LCS
static int lcs(int[] a, int[] b)
{
int n = a.length;
int m = b.length;
int[][] dp = new int[2][m + 1];
// Binary index, used to
// index current row and
// previous row.
int bi = 0;
for (int i = 0; i <= n; i++) {
// Compute current
// binary index
bi = i & 1;
for (int j = 0; j <= m; j++) {
if (i == 0 || j == 0) {
dp[bi][j] = 0;
}
else if (a[i - 1] == b[j - 1]) {
dp[bi][j] = dp[1 - bi][j - 1] + 1;
}
else {
dp[bi][j] = Math.max(dp[1 - bi][j],
dp[bi][j - 1]);
}
}
}
// Last filled entry contains
// length of LCS
// for a[0..n-1] and b[0..m-1]
return dp[bi][m];
}
static int makeSubsequences(int[] a, int[] b)
{
// Required answer is length of Array a minus
// length of longest common subsequence of a & b.
int ans = a.length - lcs(a, b);
return ans;
}
public static void main(String[] args)
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
System.out.println(makeSubsequences(a, b));
}
}
# Returns length of LCS
def lcs(a, b):
n = len(a)
m = len(b)
dp = [[0] * (m + 1) for _ in range(2)]
# Binary index, used to
# index current row and
# previous row.
bi = 0
for i in range(n + 1):
# Compute current
# binary index
bi = i & 1
for j in range(m + 1):
if i == 0 or j == 0:
dp[bi][j] = 0
elif a[i - 1] == b[j - 1]:
dp[bi][j] = dp[1 - bi][j - 1] + 1
else:
dp[bi][j] = max(dp[1 - bi][j], dp[bi][j - 1])
# Last filled entry contains
# length of LCS
# for a[0..n-1] and b[0..m-1]
return dp[bi][m]
def makeSubsequences(a, b):
# Required answer is length of Array a minus
# length of longest common subsequence of a & b.
ans = len(a) - lcs(a, b)
return ans
if __name__ == "__main__":
a = [1, 2, 3, 4, 5]
b = [2, 5, 6, 4, 9, 12]
print(makeSubsequences(a, b))
using System;
public class GFG {
// Returns length of LCS
static int lcs(int[] a, int[] b)
{
int n = a.Length;
int m = b.Length;
int[, ] dp = new int[2, m + 1];
// Binary index, used to
// index current row and
// previous row.
int bi = 0;
for (int i = 0; i <= n; i++) {
// Compute current binary index
bi = i & 1;
for (int j = 0; j <= m; j++) {
if (i == 0 || j == 0) {
dp[bi, j] = 0;
}
else if (a[i - 1] == b[j - 1]) {
dp[bi, j] = dp[1 - bi, j - 1] + 1;
}
else {
dp[bi, j] = Math.Max(dp[1 - bi, j],
dp[bi, j - 1]);
}
}
}
// Last filled entry contains
// length of LCS
// for a[0..n-1] and b[0..m-1]
return dp[bi, m];
}
static int makeSubsequences(int[] a, int[] b)
{
// Required answer is length of Array a minus
// length of longest common subsequence of a & b.
int ans = a.Length - lcs(a, b);
return ans;
}
public static void Main()
{
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 2, 5, 6, 4, 9, 12 };
Console.WriteLine(makeSubsequences(a, b));
}
}
function lcs(a, b)
{
// Returns length of LCS
let n = a.length;
let m = b.length;
let dp = new Array(2).fill(null).map(
() => new Array(m + 1).fill(0));
// Binary index, used to
// index current row and
// previous row.
let bi;
for (let i = 0; i <= n; i++) {
// Compute current
// binary index
bi = i % 2 === 1;
for (let j = 0; j <= m; j++) {
if (i === 0 || j === 0)
dp[bi ? 1 : 0][j] = 0;
else if (a[i - 1] === b[j - 1])
dp[bi ? 1 : 0][j]
= dp[bi ? 0 : 1][j - 1] + 1;
else
dp[bi ? 1 : 0][j]
= Math.max(dp[bi ? 0 : 1][j],
dp[bi ? 1 : 0][j - 1]);
}
}
// Last filled entry contains
// length of LCS
// for a[0..n-1] and b[0..m-1]
return dp[bi ? 1 : 0][m];
}
function makeSubsequences(a, b)
{
// Required answer is length of Array a minus
// length of longest common subsequence of a & b.
let ans = a.length - lcs(a, b);
return ans;
}
// Driver Code
let a = [ 1, 2, 3, 4, 5 ];
let b = [ 2, 5, 6, 4, 9, 12 ];
console.log(makeSubsequences(a, b));
Output
3