Finding minimum flips in a binary string using JavaScript

A monotonically increasing binary string consists of some number of '0's (possibly zero) followed by some number of '1's (also possibly zero). Examples include "000", "111", "0011", or "01".

Problem Statement

Given a binary string, we need to find the minimum number of flips required to make it monotonically increasing. We can flip any '0' to '1' or any '1' to '0'.

For example, with input "00110", we can flip the last '0' to '1' to get "00111", which requires only 1 flip.

Approach Using Dynamic Programming

We use memoization to track the minimum flips needed at each position. For each character, we have two choices: keep it as is or flip it. The key insight is that once we encounter a '1', all subsequent characters should ideally be '1' to maintain monotonic order.

const str = '00110';
const countFlips = (str = '') => {
    const memo = {};
    
    const helper = (index, prevChar) => {
        // Create memoization key
        const key = `${index}_${prevChar}`;
        if (memo[key] !== undefined) {
            return memo[key];
        }
        
        // Base case: reached end of string
        if (index >= str.length) {
            return 0;
        }
        
        const currentChar = str[index];
        let result;
        
        if (prevChar === '0') {
            // We can have either '0' or '1' next
            if (currentChar === '0') {
                // Keep as '0' or flip to '1'
                result = Math.min(
                    helper(index + 1, '0'),     // Keep as '0'
                    helper(index + 1, '1') + 1  // Flip to '1'
                );
            } else {
                // currentChar is '1'
                // Keep as '1' or flip to '0'
                result = Math.min(
                    helper(index + 1, '1'),     // Keep as '1'
                    helper(index + 1, '0') + 1  // Flip to '0'
                );
            }
        } else {
            // prevChar is '1', so we must have '1' from here on
            if (currentChar === '0') {
                // Must flip '0' to '1'
                result = helper(index + 1, '1') + 1;
            } else {
                // Keep '1' as is
                result = helper(index + 1, '1');
            }
        }
        
        memo[key] = result;
        return result;
    };
    
    return helper(0, '0');
};

console.log(countFlips(str));
1

How It Works

The algorithm uses recursive backtracking with memoization:

  1. At each position, we consider the previous character state
  2. If previous was '0', we can choose to keep the current character or flip it
  3. If previous was '1', we must ensure all subsequent characters are '1' (flip '0's)
  4. Memoization prevents recalculating the same subproblems

Alternative Simpler Approach

A more intuitive solution counts flips needed for each possible split point:

function minFlipsSimple(s) {
    const n = s.length;
    let minFlips = n; // worst case: flip all characters
    
    // Try each possible split point
    for (let i = 0; i <= n; i++) {
        let flips = 0;
        
        // Count '1's in the left part (should be all '0's)
        for (let j = 0; j < i; j++) {
            if (s[j] === '1') flips++;
        }
        
        // Count '0's in the right part (should be all '1's)
        for (let j = i; j < n; j++) {
            if (s[j] === '0') flips++;
        }
        
        minFlips = Math.min(minFlips, flips);
    }
    
    return minFlips;
}

const testStr = '00110';
console.log(minFlipsSimple(testStr));
1

Conclusion

Both approaches solve the problem effectively. The dynamic programming solution is more complex but handles edge cases elegantly, while the simpler approach is easier to understand and implement for this specific problem.

Updated on: 2026-03-15T23:19:00+05:30

249 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements