Minimum cost to delete characters from String A to remove any subsequence as String B
Last Updated :
27 Jul, 2023
Given two strings A and B of size N and M respectively, where B is a sub-sequence of A and an array arr[] of size N, where arr[i] is the cost to delete ith character from string A. The task is to find the minimum cost to delete characters from A such that after deletion no subsequence of A is the same as B.
Examples:
Input: A = "abccd", B = "ccd", arr[] = {1, 2, 3, 4, 5}
Output: 3
Explanation: If we remove either 'd' or a single 'c' from A then it will not be possible to construct a sub-sequence equals to B.
Among these the cost to remove the 'c' at index 2 is minimum that is 3. So the answer is 3.
Input: A = "abccdabccdabccd", B = "bccd", arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
Output: 21
Explanation: If we remove the three 'b's with cost 2, 7 and 5, then A becomes "accdaccdaccd".
Now there is no way to construct a sub-sequence = "bccd"
Approach: The naive approach to solve the problem is to use recursion and to find a common subsequence between A and B as per the following idea:
If the character of A and B matches there will be 2 options: either remove that character from A or keep that character and remove other characters.
Follow the steps mentioned below to implement the idea:
- Recursively traverse through string A using pointer i and keep a pointer j to point to B.
- In each recursive function:
- If the end of the string is reached return 0 if any character is removed (checked by a counter of deleted elements) otherwise return high positive value.
- If A[i] = B[j] there are two choices:
- Remove A[i] and add cost arr[i] to answer and recursively call for the next elements.
- Keep A[i] and move forward.
- Else keep moving forward until A[i] matches B[i].
- Return the minimum cost among the above two cases from each recursive call.
- Return the minimum cost as answer.
Time Complexity: O(2M)
Auxiliary Space: O(1)
Efficient Approach: The time of the above approach can be further optimized using Dynamic Programming as per the following idea:
Use a 3D dp[][][] array to store the minimum cost until a given position in A and B and for removing at least one character. dp[i][j][] stores minimum cost to delete characters till ith index of A and jth index of B where either at least one character is deleted or not.
Hence the third dimension only has two value either 1 (at least one is deleted) or 0 (none is deleted)
Below is the implementation of the above approach.
C++
// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
// Array for memoization
int dp[101][101][2];
// Recursive function to calculate
// the minimum cost using dynamic programming
int minCostUtil(string& a, int n,
string& b, int m,
vector<int>& c, int removed)
{
// Base case reached the end of string
if (n == 0 || m == 0) {
// Removed 0 characters
// return high (+)ve value
if (removed == 0)
return 99999;
return 0;
}
// Return pre-calculated value
if (dp[n][m][removed > 0 ? 1 : 0] != -1)
return dp[n][m][removed > 0 ? 1 : 0];
// If characters match return the minimum of
// 1. Removing the character from A and
// adding the cost
// 2. Moving forward to remove some other
// character and decrease the counter as
// this character will not be removed.
if (a[n - 1] == b[m - 1]) {
dp[n][m][removed > 0 ? 1 : 0]
= min(c[n - 1]
+ minCostUtil(a, n - 1,
b, m, c, removed),
minCostUtil(a, n - 1, b, m - 1,
c, removed - 1));
return dp[n][m][removed > 0 ? 1 : 0];
}
// If no match then move through string
// A and try removing some other
// character which matches, i.e can be
// part of the subsequence that is equal to B
else
return dp[n][m][removed > 0 ? 1 : 0]
= minCostUtil(a, n - 1,
b, m, c, removed);
}
// Function to calculate minimum cost
int minCost(string& a, string& b,
vector<int>& c)
{
memset(dp, -1, sizeof(dp));
return minCostUtil(a, a.size(), b,
b.size(), c, b.size());
}
// Driver code
int main()
{
string A = "abccdabccdabccd";
string B = "bccd";
vector<int> arr = { 1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15 };
cout << minCost(A, B, arr);
return 0;
}
Java
// Java program for the above approach
import java.util.*;
public class GFG {
// Array for memoization
static int dp[][][] = new int[101][101][2];
// Recursive function to calculate
// the minimum cost using dynamic programming
static int minCostUtil(String a, int n, String b, int m,
int[] c, int removed)
{
// Base case reached the end of string
if (n == 0 || m == 0) {
// Removed 0 characters
// return high (+)ve value
if (removed == 0)
return 99999;
return 0;
}
// Return pre-calculated value
if (dp[n][m][removed > 0 ? 1 : 0] != -1)
return dp[n][m][removed > 0 ? 1 : 0];
// If characters match return the minimum of
// 1. Removing the character from A and
// adding the cost
// 2. Moving forward to remove some other
// character and decrease the counter as
// this character will not be removed.
if (a.charAt(n - 1) == b.charAt(m - 1)) {
dp[n][m][removed > 0 ? 1 : 0]
= Math.min(c[n - 1]
+ minCostUtil(a, n - 1, b, m,
c, removed),
minCostUtil(a, n - 1, b, m - 1,
c, removed - 1));
return dp[n][m][removed > 0 ? 1 : 0];
}
// If no match then move through string
// A and try removing some other
// character which matches, i.e can be
// part of the subsequence that is equal to B
else
return dp[n][m][removed > 0 ? 1 : 0]
= minCostUtil(a, n - 1, b, m, c, removed);
}
// Function to calculate minimum cost
static int minCost(String a, String b, int[] c)
{
for (int i = 0; i < 101; i++) {
for (int j = 0; j < 101; j++) {
for (int k = 0; k < 2; k++) {
dp[i][j][k] = -1;
}
}
}
return minCostUtil(a, a.length(), b, b.length(), c,
b.length());
}
// Driver code
public static void main(String args[])
{
String A = "abccdabccdabccd";
String B = "bccd";
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15 };
System.out.print(minCost(A, B, arr));
}
}
// This code is contributed by Samim Hossain Mondal.
Python3
# Python3 program for the above approach
# Array for memoization
dp = []
# recursive function to calculate the
# minimum cost using dynamic programming
def minCostUtil(a, n, b, m, c, removed):
global dp
# Base Case - reached the end of the string
if n == 0 or m == 0:
# removed 0 characters
# return high +ve value
if removed == 0:
return 99999
return 0
# return pre - calculated value
if dp[n][m][int(bool(removed))] != -1:
return dp[n][m][int(bool(removed))]
# 1. Removing the character from A and
# adding the cost
# 2. Moving forward to remove some other
# character and decrease the counter as
# this character will not be removed.
if a[n - 1] == b[m - 1]:
dp[n][m][int(bool(removed))] = min(c[n - 1] + minCostUtil(a, n - 1,
b, m, c, removed), minCostUtil(a, n - 1, b, m - 1, c, removed - 1))
return dp[n][m][int(bool(removed))]
# if no match, then move through string
# A and try removing some other
# character which matches, ie, can be
# part of the subsequence that is equal to B
else:
dp[n][m][int(bool(removed))] = minCostUtil(a, n - 1, b, m, c, removed)
return dp[n][m][int(bool(removed))]
# function to calculate minimum bed
def minCost(a, b, c):
global dp
for i in range(101):
dp.append([])
for j in range(101):
dp[i].append([])
for k in range(2):
dp[i][j].append(-1)
return minCostUtil(a, len(a), b, len(b), c, len(b))
# Driver Code
A = "abccdabccdabccd"
B = "bccd"
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
print(minCost(A, B, arr))
# This code is contributed by phasing17
C#
// C# program for the above approach
using System;
public class GFG{
// Array for memoization
static int[,,] dp = new int[101, 101, 2];
// Recursive function to calculate
// the minimum cost using dynamic programming
static int minCostUtil(string a, int n, string b, int m,
int[] c, int removed)
{
// Base case reached the end of string
if (n == 0 || m == 0) {
// Removed 0 characters
// return high (+)ve value
if (removed == 0)
return 99999;
return 0;
}
// Return pre-calculated value
if (dp[n, m, removed > 0 ? 1 : 0] != -1)
return dp[n, m, removed > 0 ? 1 : 0];
// If characters match return the minimum of
// 1. Removing the character from A and
// adding the cost
// 2. Moving forward to remove some other
// character and decrease the counter as
// this character will not be removed.
if (a[n - 1] == b[m - 1]) {
dp[n, m, removed > 0 ? 1 : 0]
= Math.Min(c[n - 1]
+ minCostUtil(a, n - 1, b, m,
c, removed),
minCostUtil(a, n - 1, b, m - 1,
c, removed - 1));
return dp[n, m, removed > 0 ? 1 : 0];
}
// If no match then move through string
// A and try removing some other
// character which matches, i.e can be
// part of the subsequence that is equal to B
else
return dp[n, m, removed > 0 ? 1 : 0]
= minCostUtil(a, n - 1, b, m, c, removed);
}
// Function to calculate minimum cost
static int minCost(string a, string b, int[] c)
{
for (int i = 0; i < 101; i++) {
for (int j = 0; j < 101; j++) {
for (int k = 0; k < 2; k++) {
dp[i, j, k] = -1;
}
}
}
return minCostUtil(a, a.Length, b, b.Length, c,
b.Length);
}
// Driver code
static public void Main (){
string A = "abccdabccdabccd";
string B = "bccd";
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15 };
Console.Write(minCost(A, B, arr));
}
}
// This code is contributed by hrithikgarg03188.
JavaScript
// JavaScript program for the above approach
// Array for memoization
var dp = [];
// Recursive function to calculate
// the minimum cost using dynamic programming
function minCostUtil(a, n, b, m, c, removed)
{
// Base case reached the end of string
if (n == 0 || m == 0) {
// Removed 0 characters
// return high (+)ve value
if (removed == 0)
return 99999;
return 0;
}
// Return pre-calculated value
if (dp[n][m][Number(Boolean(removed))] != -1)
return dp[n][m][(removed > 0) ? 1 : 0];
// If characters match return the minimum of
// 1. Removing the character from A and
// adding the cost
// 2. Moving forward to remove some other
// character and decrease the counter as
// this character will not be removed.
if (a[n - 1] == b[m - 1]) {
dp[n][m][(removed > 0) ? 1 : 0] = Math.min(
c[n - 1]
+ minCostUtil(a, n - 1, b, m, c, removed),
minCostUtil(a, n - 1, b, m - 1, c,
removed - 1));
return dp[n][m][(removed > 0) ? 1 : 0];
}
// If no match then move through string
// A and try removing some other
// character which matches, i.e can be
// part of the subsequence that is equal to B
else
return dp[n][m][(removed > 0) ? 1 : 0]
= minCostUtil(a, n - 1, b, m, c, removed);
}
// Function to calculate minimum cost
function minCost(a, b, c)
{
for (var i = 0; i < 101; i++) {
dp[i] = [];
for (var j = 0; j < 101; j++) {
dp[i].push([]);
for (var k = 0; k < 2; k++) {
dp[i][j].push([-1]);
}
}
}
return minCostUtil(a, a.length, b, b.length, c,
b.length);
}
// Driver code
var A = "abccdabccdabccd";
var B = "bccd";
var arr =
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ];
document.write(minCost(A, B, arr));
// This code is contributed by phasing17
Time Complexity: O(N*M)
Auxiliary Space: O(N*M)
Iterative approach : Using DP Tabulation method
The approach to solve this problem is same but DP tabulation(bottom-up) method is better then Dp + memorization(top-down) because memorization method needs extra stack space of recursion calls.
Steps to solve this problem :
- Create a 3D dynamic programming table dp[n+1][m+1][m+1], where n is the length of string 'a', m is the length of string 'b', and the third dimension represents the number of characters to be removed.
- Initialize base cases:
a. If either i or j equals 0, then if k equals 0, set dp[i][j][k] to INT_MAX, else set it to 0.
b. If i and j are greater than 0, set dp[i][j][k] to INT_MAX. - Tabulate the subproblems:
a. Iterate over i from 1 to n, j from 1 to m, and k from 1 to removed.
b. If a[i-1] equals b[j-1], set dp[i][j][k] to min(c[i-1] + dp[i-1][j][k], dp[i-1][j-1][k-1]).
c. Else, set dp[i][j][k] to dp[i-1][j][k]. - Find the minimum cost of removing m characters from a:
a. Initialize ans as INT_MAX.
b. Iterate over k from 1 to removed, and set ans to min(ans, dp[n][m][k]). - Return ans if it is less than INT_MAX, else return -1.
Implementation :
C++
// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
// This function takes in two strings a and b and a vector of integers c
// It returns the minimum cost of removing m characters
// from a such that it becomes b
int minCost(string& a, string& b, vector<int>& c) {
int n = a.size();
int m = b.size();
int removed = m;
// 3D dp array
int dp[n+1][m+1][m+1];
// base cases
for(int i=0; i<=n; i++) {
for(int j=0; j<=m; j++) {
for(int k=0; k<=m; k++) {
if(i==0 || j==0) {
if(k == 0) {
// if both strings are empty and no characters
// need to be removed, then cost is infinity
dp[i][j][k] = INT_MAX;
}
else {
// if either of the strings is empty,
// no cost is required to remove any characters
dp[i][j][k] = 0;
}
}
else {
dp[i][j][k] = INT_MAX;
}
}
}
}
// tabulate the subproblems
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
for(int k=1; k<=removed; k++) {
if(a[i-1] == b[j-1]) {
// if the characters match, we can either remove
// the character from string a or not remove it
// the minimum cost is stored in the dp array
dp[i][j][k] = min(c[i-1] + dp[i-1][j][k], dp[i-1][j-1][k-1]);
}
else {
// if the characters do not match, we can
// only remove the character from string a
dp[i][j][k] = dp[i-1][j][k];
}
}
}
}
// find the minimum cost of removing m characters from a
int ans = INT_MAX;
for(int k=1; k<=removed; k++) {
ans = min(ans, dp[n][m][k]);
}
return ans == INT_MAX ? -1 : ans;
}
// Driver code
int main()
{
string A = "abccdabccdabccd";
string B = "bccd";
vector<int> arr = { 1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15 };
cout << minCost(A, B, arr);
return 0;
}
// this code is contributed by bhardwajji
Java
import java.util.Arrays;
public class Main {
// This function takes in two strings a and b and a vector of integers c
// It returns the minimum cost of removing m characters
// from a such that it becomes b
public static int minCost(String a, String b, int[] c) {
int n = a.length();
int m = b.length();
int removed = m;
// 3D dp array
int[][][] dp = new int[n+1][m+1][m+1];
// base cases
for (int[][] i : dp) {
for (int[] j : i) {
Arrays.fill(j, Integer.MAX_VALUE);
}
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= m; k++) {
if (i == 0 || j == 0) {
if (k == 0) {
// if both strings are empty and no characters
// need to be removed, then cost is infinity
dp[i][j][k] = Integer.MAX_VALUE;
} else {
// if either of the strings is empty,
// no cost is required to remove any characters
dp[i][j][k] = 0;
}
} else {
dp[i][j][k] = Integer.MAX_VALUE;
}
}
}
}
// tabulate the subproblems
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 1; k <= removed; k++) {
if (a.charAt(i-1) == b.charAt(j-1)) {
// if the characters match, we can either remove
// the character from string a or not remove it
// the minimum cost is stored in the dp array
dp[i][j][k] = Math.min(c[i-1] + dp[i-1][j][k], dp[i-1][j-1][k-1]);
} else {
// if the characters do not match, we can
// only remove the character from string a
dp[i][j][k] = dp[i-1][j][k];
}
}
}
}
// find the minimum cost of removing m characters from a
int ans = Integer.MAX_VALUE;
for (int k = 1; k <= removed; k++) {
ans = Math.min(ans, dp[n][m][k]);
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// Driver code
public static void main(String[] args) {
String A = "abccdabccdabccd";
String B = "bccd";
int[] arr = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15};
System.out.println(minCost(A, B, arr));
}
}
// This code is contributed by Shivhack999
Python3
import sys
# This function takes in two strings a and b and a vector of integers c
# It returns the minimum cost of removing m characters
# from a such that it becomes b
def minCost(a, b, c):
n = len(a)
m = len(b)
removed = m
# 3D dp array
dp = [[[sys.maxsize for k in range(m+1)] for j in range(n+1)] for i in range(n+1)]
# base cases
for i in range(n+1):
for j in range(m+1):
for k in range(m+1):
if i==0 or j==0:
if k == 0:
# if both strings are empty and no characters
# need to be removed, then cost is infinity
dp[i][j][k] = sys.maxsize
else:
# if either of the strings is empty,
# no cost is required to remove any characters
dp[i][j][k] = 0
else:
dp[i][j][k] = sys.maxsize
# tabulate the subproblems
for i in range(1, n+1):
for j in range(1, m+1):
for k in range(1, removed+1):
if a[i-1] == b[j-1]:
# if the characters match, we can either remove
# the character from string a or not remove it
# the minimum cost is stored in the dp array
dp[i][j][k] = min(c[i-1] + dp[i-1][j][k], dp[i-1][j-1][k-1])
else:
# if the characters do not match, we can
# only remove the character from string a
dp[i][j][k] = dp[i-1][j][k]
# find the minimum cost of removing m characters from a
ans = sys.maxsize
for k in range(1, removed+1):
ans = min(ans, dp[n][m][k])
return -1 if ans == sys.maxsize else ans
# Driver code
if __name__ == '__main__':
A = "abccdabccdabccd"
B = "bccd"
arr = [1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15]
print(minCost(A, B, arr))
C#
using System;
using System.Collections.Generic;
class GFG
{
// This function takes in two strings a and b and a vector of integers c
// It returns the minimum cost of removing m characters
// from a such that it becomes b
static int MinCost(string a, string b, List<int> c)
{
int n = a.Length;
int m = b.Length;
int removed = m;
// 3D dp array
int[,,] dp = new int[n + 1, m + 1, m + 1];
// base cases
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= m; j++)
{
for (int k = 0; k <= m; k++)
{
if (i == 0 || j == 0)
{
if (k == 0)
{
// if both strings are empty and no characters
// need to be removed, then cost is infinity
dp[i, j, k] = int.MaxValue;
}
else
{
// if either of the strings is empty,
// no cost is required to remove any characters
dp[i, j, k] = 0;
}
}
else
{
dp[i, j, k] = int.MaxValue;
}
}
}
}
// tabulate the subproblems
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int k = 1; k <= removed; k++)
{
if (a[i - 1] == b[j - 1])
{
// if the characters match, we can either remove
// the character from string a or not remove it
// the minimum cost is stored in the dp array
dp[i, j, k] = Math.Min(c[i - 1] + dp[i - 1, j, k], dp[i - 1, j - 1, k - 1]);
}
else
{
// if the characters do not match, we can
// only remove the character from string a
dp[i, j, k] = dp[i - 1, j, k];
}
}
}
}
// find the minimum cost of removing m characters from a
int ans = int.MaxValue;
for (int k = 1; k <= removed; k++)
{
ans = Math.Min(ans, dp[n, m, k]);
}
return ans == int.MaxValue ? -1 : ans;
}
// Driver code
static void Main(string[] args)
{
string A = "abccdabccdabccd";
string B = "bccd";
List<int> arr = new List<int> { 1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15 };
Console.WriteLine(MinCost(A, B, arr));
}
}
JavaScript
// JavaScript program for the above approach
// This function takes in two strings a and b and a vector of integers c
// It returns the minimum cost of removing m characters
// from a such that it becomes b
function minCost(a, b, c) {
let n = a.length;
let m = b.length;
let removed = m;
// 3D dp array
let dp = new Array(n+1).fill(0).map(() => new Array(m+1).fill(0).map(() => new Array(m+1).fill(0)));
// base cases
for(let i=0; i<=n; i++) {
for(let j=0; j<=m; j++) {
for(let k=0; k<=m; k++) {
if(i==0 || j==0) {
if(k == 0) {
// if both strings are empty and no characters
// need to be removed, then cost is infinity
dp[i][j][k] = Number.MAX_SAFE_INTEGER;
}
else {
// if either of the strings is empty,
// no cost is required to remove any characters
dp[i][j][k] = 0;
}
}
else {
dp[i][j][k] = Number.MAX_SAFE_INTEGER;
}
}
}
}
// tabulate the subproblems
for(let i=1; i<=n; i++) {
for(let j=1; j<=m; j++) {
for(let k=1; k<=removed; k++) {
if(a[i-1] == b[j-1]) {
// if the characters match, we can either remove
// the character from string a or not remove it
// the minimum cost is stored in the dp array
dp[i][j][k] = Math.min(c[i-1] + dp[i-1][j][k], dp[i-1][j-1][k-1]);
}
else {
// if the characters do not match, we can
// only remove the character from string a
dp[i][j][k] = dp[i-1][j][k];
}
}
}
}
// find the minimum cost of removing m characters from a
let ans = Number.MAX_SAFE_INTEGER;
for(let k=1; k<=removed; k++) {
ans = Math.min(ans, dp[n][m][k]);
}
return ans == Number.MAX_SAFE_INTEGER ? -1 : ans;
}
// Driver code
let A = "abccdabccdabccd";
let B = "bccd";
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
console.log(minCost(A, B, arr));
Output:
21
Time complexity: O(n*m^2)
Auxiliary Space: O(n*m^2)