Facebook Pixel

1702. Maximum Binary String After Change

Problem Description

You are given a binary string consisting of only 0s and 1s. You can perform two types of operations any number of times:

Operation 1: Replace any occurrence of substring "00" with "10"

  • Example: "00010" β†’ "10010" (the first two 0s become "10")

Operation 2: Replace any occurrence of substring "10" with "01"

  • Example: "00010" β†’ "00001" (the "10" at the end becomes "01")

Your goal is to find the maximum binary string possible after applying these operations any number of times. A binary string x is considered greater than binary string y if the decimal value of x is greater than the decimal value of y.

The key insight is understanding how these operations work together:

  • Operation 2 can effectively move 1s towards the right (since "10" becomes "01")
  • Operation 1 can convert consecutive 0s into a pattern with more 1s (since "00" becomes "10")

By strategically using these operations, you can rearrange the string to maximize its decimal value. The optimal strategy involves moving all 1s that aren't at the beginning to the end, then converting the middle 0s to maximize the number of leading 1s, resulting in at most one 0 in the final string.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what these operations actually do to maximize our binary string.

First, observe what each operation accomplishes:

  • Operation 1 ("00" β†’ "10"): This converts two 0s into a 1 followed by a 0, effectively creating a 1 from nothing
  • Operation 2 ("10" β†’ "01"): This swaps a 1 and 0, moving the 1 to the right

To maximize a binary number, we want as many 1s as possible at the leftmost positions. So let's think strategically:

  1. Moving 1s around: Using Operation 2 repeatedly, we can "bubble" any 1 to the right. For example: "10" β†’ "01". If we have "100", we can do: "100" β†’ "010". This means we can move all 1s that aren't already at the beginning to the end of the string.

  2. Converting 0s to 1s: Once we've moved 1s out of the way, we'll have consecutive 0s in the middle. Using Operation 1, we can convert pairs of 0s: "00" β†’ "10". If we have "0000", we can do: "0000" β†’ "1000" β†’ "1010" β†’ "1100" β†’ "1110". Notice that from n consecutive 0s, we end up with (n-1) 1s followed by a single 0.

  3. The optimal pattern: Combining these insights, the optimal strategy becomes clear:

    • Keep all leading 1s in place (they're already in the best position)
    • Move all other 1s to the end using Operation 2
    • Convert the consecutive 0s in the middle to maximize 1s using Operation 1

The result will be a string with as many leading 1s as possible, followed by exactly one 0 (if there were any 0s to begin with), and then trailing 1s. This gives us the maximum possible binary value.

For example, "01101" would become "11011" through this process:

  • Original: "01101"
  • After moving middle 1s to the end: "00011"
  • After converting the two 0s: "10011"
  • Continue operations: "11011"

The key realization is that we can always achieve a configuration with at most one 0 in the string, and that 0 should be as far right as possible while maintaining all the 1s we can create.

Learn more about Greedy patterns.

Solution Approach

Based on our intuition, we know the optimal string will have at most one 0, positioned as far right as possible while maximizing leading 1s. Let's implement this insight:

Step 1: Check if there are any 0s

k = binary.find('0')
if k == -1:
    return binary

If the string contains no 0s (all 1s), it's already maximum, so we return it as-is.

Step 2: Count total 0s that will be consolidated

k += binary[k + 1 :].count('0')

Here's the key insight:

  • k initially holds the position of the first 0
  • We then count all 0s that appear after this first 0 using binary[k + 1:].count('0')
  • Adding these gives us the final position where our single 0 will end up

Why does this work?

  • All leading 1s (before the first 0) stay in place
  • All 0s in the string will be consolidated into consecutive 0s through Operation 2 (moving intervening 1s to the right)
  • These consecutive 0s will then be converted to 1s except for one remaining 0 through Operation 1
  • The position of this final 0 is at index k (original first 0 position + count of other 0s)

Step 3: Construct the result string

return '1' * k + '0' + '1' * (len(binary) - k - 1)

The final string has three parts:

  • '1' * k: All positions before index k are 1s
  • '0': Exactly one 0 at position k
  • '1' * (len(binary) - k - 1): All remaining positions are 1s

Example walkthrough: For binary = "01101":

  1. First 0 is at position 0
  2. Count of 0s after position 0 is 1 (the 0 at position 3)
  3. So k = 0 + 1 = 1
  4. Result: '1' * 1 + '0' + '1' * 3 = "10111"

This elegant solution avoids simulating the actual operations and directly computes the final optimal configuration in O(n) time with O(1) extra space.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the example binary = "01101":

Step 1: Find the first 0

  • The first 0 appears at index 0
  • So k = 0

Step 2: Count remaining 0s

  • After index 0, we look at substring "1101"
  • This contains one more 0 at position 3 of the original string
  • Count of remaining 0s = 1
  • Update k = 0 + 1 = 1

Step 3: Build the result

  • We need k ones before the single 0: '1' * 1 = "1"
  • Add the single 0: "10"
  • Add remaining ones: '1' * (5 - 1 - 1) = '1' * 3 = "111"
  • Final result: "10111"

Verification through operations: Let's verify this is achievable:

  • Start: "01101"
  • Use Operation 2 on index 2-3: "01101" β†’ "01011" (move the middle 1 right)
  • Use Operation 2 on index 1-2: "01011" β†’ "00111" (move another 1 right)
  • Use Operation 1 on index 0-1: "00111" β†’ "10111" (convert "00" to "10")

The result "10111" matches our calculated answer! The algorithm correctly identified that with 2 total 0s in the original string, we can create a result with exactly one 0 at position 1, with all other positions being 1s.

Solution Implementation

1class Solution:
2    def maximumBinaryString(self, binary: str) -> str:
3        # Find the index of the first '0' in the binary string
4        first_zero_index = binary.find('0')
5      
6        # If there are no zeros, the string is already maximum (all ones)
7        if first_zero_index == -1:
8            return binary
9      
10        # Count the number of zeros after the first zero
11        # This determines where the single '0' will be placed in the result
12        zeros_after_first = binary[first_zero_index + 1:].count('0')
13      
14        # Calculate the position where the single '0' will be placed
15        # The position is: first_zero_index + number_of_zeros_after_first
16        zero_position = first_zero_index + zeros_after_first
17      
18        # Construct the maximum binary string:
19        # - All '1's before the zero position
20        # - A single '0' at the calculated position
21        # - All '1's after the zero position
22        result = '1' * zero_position + '0' + '1' * (len(binary) - zero_position - 1)
23      
24        return result
25
1class Solution {
2    public String maximumBinaryString(String binary) {
3        // Find the index of the first '0' in the binary string
4        int firstZeroIndex = binary.indexOf('0');
5      
6        // If there are no '0's, the string is already maximum (all '1's)
7        if (firstZeroIndex == -1) {
8            return binary;
9        }
10      
11        // Count the total number of '0's in the string
12        int zeroCount = 1; // Start with 1 for the first '0' found
13        int stringLength = binary.length();
14      
15        for (int i = firstZeroIndex + 1; i < stringLength; ++i) {
16            if (binary.charAt(i) == '0') {
17                ++zeroCount;
18            }
19        }
20      
21        // Calculate the position where the single '0' should be placed
22        // This will be at index: firstZeroIndex + (zeroCount - 1)
23        int finalZeroPosition = firstZeroIndex + zeroCount - 1;
24      
25        // Create the result array with all '1's
26        char[] result = new char[stringLength];
27        Arrays.fill(result, '1');
28      
29        // Place the single '0' at the calculated position
30        result[finalZeroPosition] = '0';
31      
32        // Convert the character array to string and return
33        return String.valueOf(result);
34    }
35}
36
1class Solution {
2public:
3    string maximumBinaryString(string binary) {
4        // Find the position of the first '0' in the binary string
5        int firstZeroPos = binary.find('0');
6      
7        // If there are no '0's, the string is already maximum (all '1's)
8        if (firstZeroPos == string::npos) {
9            return binary;
10        }
11      
12        int stringLength = binary.size();
13        int zeroCount = firstZeroPos;  // Initialize with position of first '0'
14      
15        // Count the number of '0's after the first '0' position
16        for (int i = firstZeroPos + 1; i < stringLength; ++i) {
17            if (binary[i] == '0') {
18                ++zeroCount;
19            }
20        }
21      
22        // Construct the maximum binary string:
23        // - 'zeroCount' ones at the beginning
24        // - One '0' at position 'zeroCount'
25        // - Remaining positions filled with '1's
26        return string(zeroCount, '1') + '0' + string(stringLength - zeroCount - 1, '1');
27    }
28};
29
1/**
2 * Converts a binary string to its maximum possible value by applying operations.
3 * Operations allowed:
4 * - "00" -> "10" (change "00" to "10")
5 * - "10" -> "01" (change "10" to "01")
6 * 
7 * @param binary - The input binary string
8 * @returns The maximum binary string after applying operations
9 */
10function maximumBinaryString(binary: string): string {
11    // Find the index of the first '0' in the binary string
12    const firstZeroIndex: number = binary.indexOf('0');
13  
14    // If there are no zeros, the string is already at maximum (all 1s)
15    if (firstZeroIndex === -1) {
16        return binary;
17    }
18  
19    // Count the number of zeros after the first zero
20    // This determines where the final '0' will be positioned
21    const zerosAfterFirst: number = binary.slice(firstZeroIndex + 1).split('0').length - 1;
22  
23    // Calculate the position of the single '0' in the result
24    const finalZeroPosition: number = firstZeroIndex + zerosAfterFirst;
25  
26    // Build the result string:
27    // - All '1's before the final zero position
28    // - A single '0' at the calculated position
29    // - All '1's after the zero
30    const leftOnes: string = '1'.repeat(finalZeroPosition);
31    const rightOnes: string = '1'.repeat(binary.length - finalZeroPosition - 1);
32  
33    return leftOnes + '0' + rightOnes;
34}
35

Time and Space Complexity

Time Complexity: O(n)

The algorithm performs the following operations:

  • binary.find('0'): This searches for the first occurrence of '0' in the string, which takes O(n) time in the worst case.
  • binary[k + 1:].count('0'): This creates a substring from index k+1 to the end (taking O(n) time) and counts the occurrences of '0' in it (taking O(n) time).
  • String concatenation '1' * k + '0' + '1' * (len(binary) - k - 1): This creates a new string of length n, which takes O(n) time.

Overall, the time complexity is O(n) where n is the length of the input string.

Space Complexity: O(n)

The algorithm uses additional space for:

  • The substring binary[k + 1:] which can be up to O(n) characters in the worst case.
  • The output string created by concatenation, which has exactly n characters, requiring O(n) space.

Therefore, the space complexity is O(n) where n is the length of the input string.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Misunderstanding the Final Zero Position

The Mistake: Many developers incorrectly assume that the final 0 position should be calculated as:

zero_position = first_zero_index + total_zeros_in_string - 1

This comes from thinking that all zeros (including the first one) should be counted when determining the final position.

Why It's Wrong: The first zero is already at position first_zero_index. We only need to move it forward by the number of zeros that come after it, not by the total count of zeros.

Correct Approach:

zero_position = first_zero_index + zeros_after_first

Example to Illustrate: For binary = "1001010":

  • First zero at index 1
  • Zeros after first zero: 2 (at indices 2 and 4)
  • Wrong calculation: 1 + 3 - 1 = 3 β†’ Result: "1110111" (incorrect)
  • Correct calculation: 1 + 2 = 3 β†’ Result: "1110111" (correct)

Wait, both give the same answer here! Let's use binary = "00110":

  • First zero at index 0
  • Total zeros: 2
  • Zeros after first: 1
  • Wrong: 0 + 2 - 1 = 1 β†’ Would give "10111"
  • Correct: 0 + 1 = 1 β†’ Gives "10111"

Actually, let me reconsider. The real pitfall is different:

Pitfall 1: Attempting to Simulate Operations Instead of Direct Calculation

The Mistake: Trying to actually perform Operation 1 and Operation 2 repeatedly:

def maximumBinaryString(self, binary: str) -> str:
    result = list(binary)
    changed = True
    while changed:
        changed = False
        # Try Operation 1: "00" -> "10"
        for i in range(len(result) - 1):
            if result[i] == '0' and result[i+1] == '0':
                result[i] = '1'
                changed = True
                break
        # Try Operation 2: "10" -> "01"
        for i in range(len(result) - 1):
            if result[i] == '1' and result[i+1] == '0':
                result[i], result[i+1] = '0', '1'
                changed = True
    return ''.join(result)

Why It's Wrong:

  • This approach has exponential time complexity in worst cases
  • It's hard to determine the optimal order of operations
  • The solution may get stuck in local optima

Correct Approach: Use the mathematical insight that the final result will always have at most one 0, positioned based on the count of zeros in the original string.

Pitfall 2: Off-by-One Error in Final String Construction

The Mistake:

# Forgetting to subtract 1 for the zero position
result = '1' * zero_position + '0' + '1' * (len(binary) - zero_position)

Why It's Wrong: This creates a string that's one character longer than the original because we're not accounting for the '0' character when calculating the number of trailing 1s.

Correct Approach:

result = '1' * zero_position + '0' + '1' * (len(binary) - zero_position - 1)

The -1 accounts for the single '0' we've already placed.

Pitfall 3: Not Handling Edge Cases

The Mistake:

def maximumBinaryString(self, binary: str) -> str:
    first_zero = binary.find('0')
    zeros_after = binary[first_zero + 1:].count('0')
    # Direct calculation without checking if first_zero is -1
    return '1' * (first_zero + zeros_after) + '0' + '1' * (len(binary) - first_zero - zeros_after - 1)

Why It's Wrong: If the string contains no zeros (e.g., "1111"), find('0') returns -1, leading to incorrect string slicing and wrong results.

Correct Approach: Always check if zeros exist before proceeding:

if first_zero_index == -1:
    return binary
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More