Open In App

Minimize count of 0s in Binary String by changing K-size substring to 1s at most Q times

Improve
Improve
Like Article
Like
Save
Share
Report

Given a binary string S having N characters and two integers K and Q, the task is to find the minimum number of remaining zeroes after at most Q operations, such that in each operation, choose any substring of size at most K and change all its elements to 1.

Example: 

Input: S = 000111, K = 2, Q = 1
Output: 1
Explanation:
Choose the substring S[0, 1] and change all the characters to 1. The final string will be 110111 that has 1 zero remaining which is the minimum possible. 

Input: S = 0100010110, K = 3, Q = 2
Output: 2

Approach 1: Memoisation 

We can define dp[i][j] as the minimum number of zeroes in S[0:i] after j operations. We can then derive the recurrence relation using the following steps:

  1.  If K = 0 or Q = 0, we cannot perform any operations, so the answer is simply the number of zeroes in the original string.
  2.  If Q is large enough such that we can perform operations on all substrings of length at most K, then we can simply set the answer to 0.
  3.  Initialize dp[0][j] = 0 for all j, since there are no characters in S[0:0] and we have not performed any operations yet.
  4.  Initialize dp[i][0] = dp[i-1][0] + (S[i-1] == ‘0’) for all i, since the minimum number of zeroes in S[0:i] after 0 operations is simply the minimum number of zeroes in S[0:i-1] plus 1 if the i-th character is ‘0’.
  5.  For all i in the range [1, N] and for all j in the range [1, Q], we can use the following recurrence relation to compute dp[i][j]:
 dp[i][j] = min(dp[i-k][j-1], dp[i-1][j] + (S[i-1] == '0')),   where k = min(i, K).

The intuition behind this recurrence relation is that we can either perform an operation on the last K characters of S[0:i] (if i >= K) or on all the characters in S[0:i] except for the last character (if i < K). If we perform an operation on the last K characters, then we can reduce the number of zeroes in those characters by 1, and the remaining number of zeroes in S[0:i] is simply dp[i-k][j-1]. Otherwise, if we do not perform an operation on the last K characters, then the remaining number of zeroes in S[0:i] is simply dp[i-1][j] plus 1 if the i-th character is ‘0’.

The final answer is dp[N][Q].

Algorithm:

  1.    Define a function ‘minZeroes‘ that takes the binary string S, a 2D vector ‘dp‘, integers N, K and Q as input parameters.
  2.    Check for the base cases:
           a. If k or q is 0, then return the count of 0’s in the string.
           b. If q is greater than or equal to the maximum possible number of operations, return 0.
           c. If the result for current parameters is already calculated, return it from the memoization table.
  3.    If the result is not present in the memoization table, then use the recurrence relation given below to calculate the minimum number of            remaining zeroes:
                  dp[n][q] = min(minZeroes(s, dp, n – 1, k, q) + (s[n – 1] == ‘0’), minZeroes(s, dp, max(n – k, 0), k, q – 1)).
  4.    Store the calculated result in the memoization table.
  5.    Return the calculated result.
  6.    In the main function:
           a. Define the input parameters S, N, K, and Q.
           b. Create a 2D vector ‘dp‘ of size (N+1)x(Q+1), initialized with -1.
           c. Call the ‘minZeroes‘ function with the input parameters S, dp, N, K, and Q.
           d. Print the result.

Below is the implementation of the above approach: 

C++




// C++ code for the approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Function to calculate the minimum number of remaining
// zeroes in the binary string
int minZeroes(string s, vector<vector<int> >& dp, int n,
              int k, int q)
{
    // Base cases
    if (k == 0 || q == 0) {
        return count(
            // Count the number of 0s in the string
            s.begin(), s.end(), '0');
    }
 
    // If q is greater than or equal to the
    // maximum possible number of operations,
    // then no 0s will remain
    if (q >= (n + k - 1) / k) {
        return 0;
    }
 
    // If the result for current
    // parameters is already
    // calculated, return it from the
    // memoization table
    if (dp[n][q] != -1) {
        return dp[n][q];
    }
 
    // Recurrence relation to calculate the minimum number
    // of remaining zeroes
    dp[n][q] = min(
        minZeroes(s, dp, n - 1, k, q) + (s[n - 1] == '0'),
        minZeroes(s, dp, max(n - k, 0), k, q - 1));
 
    // Return the calculated result
    return dp[n][q];
}
 
// Driver's code
int main()
{
    string S = "0100010110";
    int N = S.size();
    int K = 3, Q = 2;
 
    // 2D vector to store the dp states,
    // initially having all the values
    // as INT_MAX
    vector<vector<int> > dp(N + 1, vector<int>(Q + 1, -1));
 
    // Call the function to calculate
    // the minimum number of remaining
    // zeroes and print the result
    cout << minZeroes(S, dp, N, K, Q);
    return 0;
}


Java




import java.util.Arrays;
 
public class Main {
    // Function to calculate the minimum number of remaining
    // zeroes in the binary string
    static int minZeroes(String s, int[][] dp, int n, int k, int q) {
        // Base cases
        if (k == 0 || q == 0) {
            return (int) s.chars().filter(ch -> ch == '0').count();
        }
 
        // If q is greater than or equal to the
        // maximum possible number of operations,
        // then no 0s will remain
        if (q >= (n + k - 1) / k) {
            return 0;
        }
 
        // If the result for current
        // parameters is already
        // calculated, return it from the
        // memoization table
        if (dp[n][q] != -1) {
            return dp[n][q];
        }
 
        // Recurrence relation to calculate the minimum number
        // of remaining zeroes
        dp[n][q] = Math.min(
                minZeroes(s, dp, n - 1, k, q) + (s.charAt(n - 1) == '0' ? 1 : 0),
                minZeroes(s, dp, Math.max(n - k, 0), k, q - 1)
        );
 
        // Return the calculated result
        return dp[n][q];
    }
 
    // Driver's code
    public static void main(String[] args) {
        String S = "0100010110";
        int N = S.length();
        int K = 3, Q = 2;
 
        // 2D array to store the dp states,
        // initially having all the values
        // as -1
        int[][] dp = new int[N + 1][Q + 1];
        for (int[] row : dp) {
            Arrays.fill(row, -1);
        }
 
        // Call the function to calculate
        // the minimum number of remaining
        // zeroes and print the result
        System.out.println(minZeroes(S, dp, N, K, Q));
    }
}


Python




def min_zeroes(s, dp, n, k, q):
    # Base cases
    if k == 0 or q == 0:
        return s.count('0')
 
    # If q is greater than or equal to the
    # maximum possible number of operations,
    # then no 0s will remain
    if q >= (n + k - 1) // k:
        return 0
 
    # If the result for current
    # parameters is already
    # calculated, return it from the
    # memoization table
    if dp[n][q] != -1:
        return dp[n][q]
 
    # Recurrence relation to calculate the minimum number
    # of remaining zeroes
    dp[n][q] = min(
        min_zeroes(s, dp, n - 1, k, q) + (1 if s[n - 1] == '0' else 0),
        min_zeroes(s, dp, max(n - k, 0), k, q - 1)
    )
 
    # Return the calculated result
    return dp[n][q]
 
# Driver's code
if __name__ == "__main__":
    S = "0100010110"
    N = len(S)
    K, Q = 3, 2
 
    # 2D array to store the dp states,
    # initially having all the values
    # as -1
    dp = [[-1] * (Q + 1) for _ in range(N + 1)]
 
    # Call the function to calculate
    # the minimum number of remaining
    # zeroes and print the result
    print(min_zeroes(S, dp, N, K, Q))


C#




using System;
 
public class MinZeroes {
    public static int MinZeroesCount(string s, int n, int k,
                                     int q)
    {
        int[, ] dp = new int[n + 1, q + 1];
 
        // Base case 1: If k or q is 0, return the count of
        // '0's in the string.
        if (k == 0 || q == 0) {
            return s.Split('0').Length - 1;
        }
 
        // Base case 2: If q is greater than or equal to the
        // maximum possible number of operations, return 0.
        if (q >= (n + k - 1) / k) {
            return 0;
        }
 
        // Initialize dp[i][0] for all i.
        for (int i = 1; i <= n; i++) {
            dp[i, 0]
                = dp[i - 1, 0] + (s[i - 1] == '0' ? 1 : 0);
        }
 
        // Fill in the dp table using the recurrence
        // relation.
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= q; j++) {
                int minZeroes = int.MaxValue;
 
                for (int x = 1; x <= Math.Min(i, k); x++) {
                    minZeroes = Math.Min(minZeroes,
                                         dp[i - x, j - 1]);
                }
 
                dp[i, j] = Math.Min(
                    minZeroes,
                    dp[i - 1, j]
                        + (s[i - 1] == '0' ? 1 : 0));
            }
        }
 
        return dp[n, q];
    }
 
    public static void Main(string[] args)
    {
        string S = "0100010110";
        int N = S.Length;
        int K = 3;
        int Q = 2;
 
        int result = MinZeroesCount(S, N, K, Q);
 
        Console.WriteLine(
            "Minimum number of remaining zeroes: "
            + result);
    }
}


Javascript




// Function to calculate the minimum number of remaining
// zeroes in the binary string
function minZeroes(s, dp, n, k, q) {
    // Base cases
    if (k === 0 || q === 0) {
        return s.split('0').length - 1;
    }
 
    // If q is greater than or equal to the
    // maximum possible number of operations,
    // then no 0s will remain
    if (q >= Math.ceil(n / k)) {
        return 0;
    }
 
    // If the result for current parameters is already
    // calculated, return it from the memoization table
    if (dp[n][q] !== -1) {
        return dp[n][q];
    }
 
    // Recurrence relation to calculate the minimum number
    // of remaining zeroes
    dp[n][q] = Math.min(
        minZeroes(s, dp, n - 1, k, q) + (s[n - 1] === '0' ? 1 : 0),
        minZeroes(s, dp, Math.max(n - k, 0), k, q - 1)
    );
 
    // Return the calculated result
    return dp[n][q];
}
 
// Driver's code
const S = "0100010110";
const N = S.length;
const K = 3;
const Q = 2;
 
// 2D array to store the dp states,
// initially having all the values
// as Infinity
const dp = new Array(N + 1);
for (let i = 0; i <= N; i++) {
    dp[i] = new Array(Q + 1).fill(-1);
}
 
// Call the function to calculate
// the minimum number of remaining
// zeroes and print the result
console.log(minZeroes(S, dp, N, K, Q));
 
// This code is contributed by akshitaguprzj3


Output

2






Time Complexity: O(N*Q) where N is size of input string and Q is number of operations. This is because for each i and j, the function dp(i, j) makes at most two recursive calls and each call takes constant time. Therefore, the number of unique function calls made by the algorithm is bounded by N*Q, and each call takes constant time. Hence, the overall time complexity is O(N*Q).

Auxiliary Space: O(N * Q), which is used to store the values of the subproblems in the 2D DP table. Here N is size of input string and Q is number of operations.

Approach 2: Using Dynamic Programming

Steps:

  1. Create a 2D array dp[][], where dp[i][j] represents the minimum number of zeroes in the prefix of length i of the given string after j operations. Below are the steps to follow:
  2. Initially, for all values of j in range [0, Q], dp[0][j] = 0 and for all values of i in range [0, N], dp[i][0] = count of 0 in prefix of length i.
  3. For each stated dp[i][j], there are two possible choices of operations:
    • Perform an operation on the substring S[i – K, i]. In such cases, dp[i][j] = dp[i – K][j – 1].
    • Do not perform any operation. In such cases, dp[i][j] = dp[i – 1][j] + X, where the value of X is 1 if the value of S[i – 1] = 0, Otherwise, X = 0.
  4. Therefore, the DP relation of the above problem can be stated as:

dp[i][j] = min(dp[i – k][j – 1], dp[i – 1][j] + (S[i – 1] == ‘0’)), for all i in the range [1, N] and for all j in the range [1, Q].

5. After completing the steps, the value stored at dp[N][Q] is the required answer.

Below is the implementation of the above approach: 

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Function to find the minimum number of
// zeroes after changing the value of all
// elements of any substring of at most K
// elements to 1 at most Q number of times
int minZeroeCount(string S, int K, int Q)
{
 
    // Stores the size of the given string
    int N = S.size();
 
    // If no operation is possible, return
    // the count of 0 in the initial string
    if (K == 0 || Q == 0) {
        return count(S.begin(), S.end(), '0');
    }
 
    // If all the elements of the given string
    // can be selected in Q operations
    if (Q >= ceil((float)N / K)) {
        return 0;
    }
 
    // 2D vector to store the dp states,
    // initially having all the values
    // as INT_MAX
    vector<vector<int> > dp(N + 1,
                            vector<int>(Q + 1, INT_MAX));
 
    // Loop to initialize dp[0][j] = 0
    // for all j in the range [0, Q],
    for (int j = 0; j <= Q; ++j) {
        dp[0][j] = 0;
    }
 
    // Loop to initialize dp[0][j] = count
    // of 0 in prefix of length i for all
    // i in the range [1, N],
    for (int i = 1; i <= N; ++i) {
        dp[i][0] = dp[i - 1][0] + (S[i - 1] == '0');
    }
 
    // Traverse over all dp[][] states
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= Q; ++j) {
 
            // Find the value of dp[i][j]
            dp[i][j]
                = min(dp[max(0, i - K)][j - 1],
                      dp[i - 1][j] + (S[i - 1] == '0'));
        }
    }
 
    // Return answer
    return dp[N][Q];
}
 
// Driver Code
int main()
{
    string S = "0100010110";
    int K = 3;
    int Q = 2;
 
    cout << minZeroeCount(S, K, Q);
 
    return 0;
}


Java




// Java program for the above approach
import java.io.*;
 
class GFG
{
   
      // Function to find the minimum number of
    // zeroes after changing the value of all
    // elements of any substring of at most K
    // elements to 1 at most Q number of times
    static int minZeroeCount(String S, int K, int Q)
    {
 
        // Stores the size of the given string
        int N = S.length();
 
        // If no operation is possible, return
        // the count of 0 in the initial string
        if (K == 0 || Q == 0)
        {
              int c = 0;
            for(int i = 0; i < S.length(); i++) {
                  if(S.charAt(i) == '0')
                  c++;
            }
              return c;
        }
 
        // If all the elements of the given string
        // can be selected in Q operations
        if (Q >= Math.ceil((float)N / K)) {
            return 0;
        }
 
        // 2D vector to store the dp states,
        // initially having all the values
        // as INT_MAX
        int[][] dp = new int[N + 1][Q + 1];
 
        // Loop to initialize dp[0][j] = 0
        // for all j in the range [0, Q],
        for (int j = 0; j <= Q; ++j) {
            dp[0][j] = 0;
        }
 
        // Loop to initialize dp[0][j] = count
        // of 0 in prefix of length i for all
        // i in the range [1, N],
        for (int i = 1; i <= N; ++i) {
            if (S.charAt(i - 1) == '0')
                dp[i][0] = dp[i - 1][0] + 1;
            else
                dp[i][0] = dp[i - 1][0];
        }
 
        // Traverse over all dp[][] states
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= Q; ++j) {
 
                // Find the value of dp[i][j]
 
                if (S.charAt(i - 1) == '0')
                    dp[i][j] = Math.min(
                        dp[(Math.max(0, i - K))][j - 1],
                        dp[i - 1][j] + 1);
                else
                    dp[i][j] = Math.min(
                        dp[(Math.max(0, i - K))][j - 1],
                        dp[i - 1][j]);
            }
        }
 
        // Return answer
        return dp[N][Q];
    }
 
    // Driver Code
    public static void main (String[] args) {
        String S = "0100010110";
        int K = 3;
        int Q = 2;
 
        System.out.println(minZeroeCount(S, K, Q));
    }
}
 
// This code is contributed by Dharanendra L V.


Python3




# Python 3 program for the above approach
import sys
def cnt(s,ch):
    count  = 0
    for x in s:
        if x == ch:
            count += 1
    return count
 
# Function to find the minimum number of
# zeroes after changing the value of all
# elements of any substring of at most K
# elements to 1 at most Q number of times
def minZeroeCount(S, K, Q):
   
    # Stores the size of the given string
    N = len(S)
 
    # If no operation is possible, return
    # the count of 0 in the initial string
    if (K == 0 or Q == 0):
        return cnt(S,'0')
 
    # If all the elements of the given string
    # can be selected in Q operations
    if (Q >= int(N/K) + 1):
        return 0
 
    # 2D vector to store the dp states,
    # initially having all the values
    # as INT_MAX
    dp = [[sys.maxsize for i in range(Q + 1)] for j in range(N + 1)]
 
    # Loop to initialize dp[0][j] = 0
    # for all j in the range [0, Q],
    for j in range(Q + 1):
        dp[0][j] = 0
 
    # Loop to initialize dp[0][j] = count
    # of 0 in prefix of length i for all
    # i in the range [1, N],
    for i in range(1,N+1,1):
        dp[i][0] = dp[i - 1][0] + (S[i - 1] == '0')
 
    # Traverse over all dp[][] states
    for i in range(1, N + 1, 1):
        for j in range(1, Q + 1, 1):
           
            # Find the value of dp[i][j]
            dp[i][j] = min(dp[max(0, i - K)][j - 1], dp[i - 1][j]+(S[i - 1] == '0'))
 
    # Return answer
    return dp[N][Q]
 
# Driver Code
if __name__ == '__main__':
    S = "0100010110"
    K = 3
    Q = 2
    print(minZeroeCount(S, K, Q))
     
    # This code is contributed by SURENDRA_GANGWAR.


C#




// C# program for the above approach
using System;
using System.Linq;
class GFG {
    // Function to find the minimum number of
    // zeroes after changing the value of all
    // elements of any substring of at most K
    // elements to 1 at most Q number of times
    static int minZeroeCount(string S, int K, int Q)
    {
 
        // Stores the size of the given string
        int N = S.Length;
 
        // If no operation is possible, return
        // the count of 0 in the initial string
        if (K == 0 || Q == 0)
        {
            return S.Count(c => c == '0');
        }
 
        // If all the elements of the given string
        // can be selected in Q operations
        if (Q >= Math.Ceiling((float)N / K)) {
            return 0;
        }
 
        // 2D vector to store the dp states,
        // initially having all the values
        // as INT_MAX
        int[, ] dp = new int[N + 1, Q + 1];
 
        // Loop to initialize dp[0][j] = 0
        // for all j in the range [0, Q],
        for (int j = 0; j <= Q; ++j) {
            dp[0, j] = 0;
        }
 
        // Loop to initialize dp[0][j] = count
        // of 0 in prefix of length i for all
        // i in the range [1, N],
        for (int i = 1; i <= N; ++i) {
            if (S[i - 1] == '0')
                dp[i, 0] = dp[i - 1, 0] + 1;
            else
                dp[i, 0] = dp[i - 1, 0];
        }
 
        // Traverse over all dp[][] states
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= Q; ++j) {
 
                // Find the value of dp[i][j]
 
                if (S[i - 1] == '0')
                    dp[i, j] = Math.Min(
                        dp[(Math.Max(0, i - K)), j - 1],
                        dp[i - 1, j] + 1);
                else
                    dp[i, j] = Math.Min(
                        dp[(Math.Max(0, i - K)), j - 1],
                        dp[i - 1, j]);
            }
        }
 
        // Return answer
        return dp[N, Q];
    }
 
    // Driver Code
    public static void Main()
    {
        string S = "0100010110";
        int K = 3;
        int Q = 2;
 
        Console.WriteLine(minZeroeCount(S, K, Q));
    }
}
 
// This code is contributed by ukasp.


Javascript




<script>
 
    // JavaScript program for the above approach
 
    const INT_MAX = 2147483647;
    // Function to find the minimum number of
    // zeroes after changing the value of all
    // elements of any substring of at most K
    // elements to 1 at most Q number of times
    const minZeroeCount = (S, K, Q) => {
 
        // Stores the size of the given string
        let N = S.length;
 
        // If no operation is possible, return
        // the count of 0 in the initial string
        if (K == 0 || Q == 0) {
            let cnt0 = 0;
            for (let i = 0; i < N; ++i) {
                if (S[i] == '0') cnt0++;
            }
            return cnt0;
        }
 
        // If all the elements of the given string
        // can be selected in Q operations
        if (Q >= Math.ceil(N / K)) {
            return 0;
        }
 
        // 2D vector to store the dp states,
        // initially having all the values
        // as INT_MAX
        const dp = new Array(N + 1).fill(INT_MAX).map(() => new Array(Q + 1).fill(INT_MAX));
 
        // Loop to initialize dp[0][j] = 0
        // for all j in the range [0, Q],
        for (let j = 0; j <= Q; ++j) {
            dp[0][j] = 0;
        }
 
        // Loop to initialize dp[0][j] = count
        // of 0 in prefix of length i for all
        // i in the range [1, N],
        for (let i = 1; i <= N; ++i) {
            dp[i][0] = dp[i - 1][0] + (S[i - 1] == '0');
        }
 
        // Traverse over all dp[][] states
        for (let i = 1; i <= N; ++i) {
            for (let j = 1; j <= Q; ++j) {
 
                // Find the value of dp[i][j]
                dp[i][j]
                    = Math.min(dp[(Math.max(0, i - K))][j - 1], dp[i - 1][j] + (S[i - 1] == '0'));
            }
        }
 
        // Return answer
        return dp[N][Q];
    }
 
    // Driver Code
    let S = "0100010110";
    let K = 3;
    let Q = 2;
 
    document.write(minZeroeCount(S, K, Q));
 
    // This code is contributed by rakeshsahni
 
</script>


 
 

Output

2






 Time Complexity: O(N*Q)
Auxiliary Space: O(N*Q)



Last Updated : 07 Nov, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads