Minimize count of 0s in Binary String by changing K-size substring to 1s at most Q times
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:
- If K = 0 or Q = 0, we cannot perform any operations, so the answer is simply the number of zeroes in the original string.
- 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.
- 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.
- 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’.
- 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:
- Define a function ‘minZeroes‘ that takes the binary string S, a 2D vector ‘dp‘, integers N, K and Q as input parameters.
- 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.
- 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)).
- Store the calculated result in the memoization table.
- Return the calculated result.
- 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++
#include <bits/stdc++.h>
using namespace std;
int minZeroes(string s, vector<vector< int > >& dp, int n,
int k, int q)
{
if (k == 0 || q == 0) {
return count(
s.begin(), s.end(), '0' );
}
if (q >= (n + k - 1) / k) {
return 0;
}
if (dp[n][q] != -1) {
return dp[n][q];
}
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 dp[n][q];
}
int main()
{
string S = "0100010110" ;
int N = S.size();
int K = 3, Q = 2;
vector<vector< int > > dp(N + 1, vector< int >(Q + 1, -1));
cout << minZeroes(S, dp, N, K, Q);
return 0;
}
|
Java
import java.util.Arrays;
public class Main {
static int minZeroes(String s, int [][] dp, int n, int k, int q) {
if (k == 0 || q == 0 ) {
return ( int ) s.chars().filter(ch -> ch == '0' ).count();
}
if (q >= (n + k - 1 ) / k) {
return 0 ;
}
if (dp[n][q] != - 1 ) {
return dp[n][q];
}
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 dp[n][q];
}
public static void main(String[] args) {
String S = "0100010110" ;
int N = S.length();
int K = 3 , Q = 2 ;
int [][] dp = new int [N + 1 ][Q + 1 ];
for ( int [] row : dp) {
Arrays.fill(row, - 1 );
}
System.out.println(minZeroes(S, dp, N, K, Q));
}
}
|
Python
def min_zeroes(s, dp, n, k, q):
if k = = 0 or q = = 0 :
return s.count( '0' )
if q > = (n + k - 1 ) / / k:
return 0
if dp[n][q] ! = - 1 :
return dp[n][q]
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 dp[n][q]
if __name__ = = "__main__" :
S = "0100010110"
N = len (S)
K, Q = 3 , 2
dp = [[ - 1 ] * (Q + 1 ) for _ in range (N + 1 )]
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];
if (k == 0 || q == 0) {
return s.Split( '0' ).Length - 1;
}
if (q >= (n + k - 1) / k) {
return 0;
}
for ( int i = 1; i <= n; i++) {
dp[i, 0]
= dp[i - 1, 0] + (s[i - 1] == '0' ? 1 : 0);
}
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 minZeroes(s, dp, n, k, q) {
if (k === 0 || q === 0) {
return s.split( '0' ).length - 1;
}
if (q >= Math.ceil(n / k)) {
return 0;
}
if (dp[n][q] !== -1) {
return dp[n][q];
}
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 dp[n][q];
}
const S = "0100010110" ;
const N = S.length;
const K = 3;
const Q = 2;
const dp = new Array(N + 1);
for (let i = 0; i <= N; i++) {
dp[i] = new Array(Q + 1).fill(-1);
}
console.log(minZeroes(S, dp, N, K, Q));
|
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:
- 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:
- 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.
- 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.
- 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++
#include <bits/stdc++.h>
using namespace std;
int minZeroeCount(string S, int K, int Q)
{
int N = S.size();
if (K == 0 || Q == 0) {
return count(S.begin(), S.end(), '0' );
}
if (Q >= ceil (( float )N / K)) {
return 0;
}
vector<vector< int > > dp(N + 1,
vector< int >(Q + 1, INT_MAX));
for ( int j = 0; j <= Q; ++j) {
dp[0][j] = 0;
}
for ( int i = 1; i <= N; ++i) {
dp[i][0] = dp[i - 1][0] + (S[i - 1] == '0' );
}
for ( int i = 1; i <= N; ++i) {
for ( int j = 1; j <= Q; ++j) {
dp[i][j]
= min(dp[max(0, i - K)][j - 1],
dp[i - 1][j] + (S[i - 1] == '0' ));
}
}
return dp[N][Q];
}
int main()
{
string S = "0100010110" ;
int K = 3;
int Q = 2;
cout << minZeroeCount(S, K, Q);
return 0;
}
|
Java
import java.io.*;
class GFG
{
static int minZeroeCount(String S, int K, int Q)
{
int N = S.length();
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 (Q >= Math.ceil(( float )N / K)) {
return 0 ;
}
int [][] dp = new int [N + 1 ][Q + 1 ];
for ( int j = 0 ; j <= Q; ++j) {
dp[ 0 ][j] = 0 ;
}
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 ];
}
for ( int i = 1 ; i <= N; ++i) {
for ( int j = 1 ; j <= Q; ++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 dp[N][Q];
}
public static void main (String[] args) {
String S = "0100010110" ;
int K = 3 ;
int Q = 2 ;
System.out.println(minZeroeCount(S, K, Q));
}
}
|
Python3
import sys
def cnt(s,ch):
count = 0
for x in s:
if x = = ch:
count + = 1
return count
def minZeroeCount(S, K, Q):
N = len (S)
if (K = = 0 or Q = = 0 ):
return cnt(S, '0' )
if (Q > = int (N / K) + 1 ):
return 0
dp = [[sys.maxsize for i in range (Q + 1 )] for j in range (N + 1 )]
for j in range (Q + 1 ):
dp[ 0 ][j] = 0
for i in range ( 1 ,N + 1 , 1 ):
dp[i][ 0 ] = dp[i - 1 ][ 0 ] + (S[i - 1 ] = = '0' )
for i in range ( 1 , N + 1 , 1 ):
for j in range ( 1 , Q + 1 , 1 ):
dp[i][j] = min (dp[ max ( 0 , i - K)][j - 1 ], dp[i - 1 ][j] + (S[i - 1 ] = = '0' ))
return dp[N][Q]
if __name__ = = '__main__' :
S = "0100010110"
K = 3
Q = 2
print (minZeroeCount(S, K, Q))
|
C#
using System;
using System.Linq;
class GFG {
static int minZeroeCount( string S, int K, int Q)
{
int N = S.Length;
if (K == 0 || Q == 0)
{
return S.Count(c => c == '0' );
}
if (Q >= Math.Ceiling(( float )N / K)) {
return 0;
}
int [, ] dp = new int [N + 1, Q + 1];
for ( int j = 0; j <= Q; ++j) {
dp[0, j] = 0;
}
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];
}
for ( int i = 1; i <= N; ++i) {
for ( int j = 1; j <= Q; ++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 dp[N, Q];
}
public static void Main()
{
string S = "0100010110" ;
int K = 3;
int Q = 2;
Console.WriteLine(minZeroeCount(S, K, Q));
}
}
|
Javascript
<script>
const INT_MAX = 2147483647;
const minZeroeCount = (S, K, Q) => {
let N = S.length;
if (K == 0 || Q == 0) {
let cnt0 = 0;
for (let i = 0; i < N; ++i) {
if (S[i] == '0' ) cnt0++;
}
return cnt0;
}
if (Q >= Math.ceil(N / K)) {
return 0;
}
const dp = new Array(N + 1).fill(INT_MAX).map(() => new Array(Q + 1).fill(INT_MAX));
for (let j = 0; j <= Q; ++j) {
dp[0][j] = 0;
}
for (let i = 1; i <= N; ++i) {
dp[i][0] = dp[i - 1][0] + (S[i - 1] == '0' );
}
for (let i = 1; i <= N; ++i) {
for (let j = 1; j <= Q; ++j) {
dp[i][j]
= Math.min(dp[(Math.max(0, i - K))][j - 1], dp[i - 1][j] + (S[i - 1] == '0' ));
}
}
return dp[N][Q];
}
let S = "0100010110" ;
let K = 3;
let Q = 2;
document.write(minZeroeCount(S, K, Q));
</script>
|
Time Complexity: O(N*Q)
Auxiliary Space: O(N*Q)
Last Updated :
07 Nov, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...