Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

761. Special Binary String - Leetcode Solution

Problem Description

You are given a special binary string as input, which is a binary string consisting only of '0's and '1's. A string is considered special if it is non-empty, has the same number of '1's and '0's, and for every prefix of the string, the number of '1's is greater than or equal to the number of '0's.

Your task is to rearrange the special substrings of the input string to create the lexicographically largest possible special binary string. You can decompose the string into special substrings, rearrange them in any order, and then concatenate them back together.

Constraints:

  • The input string consists only of '0' and '1'.
  • The length of the string is between 1 and 50.
  • The input string is guaranteed to be a special binary string.

Thought Process

At first glance, the problem may seem to ask for all possible permutations of substrings, but the constraints of "special binary string" mean we can't just swap any characters. Instead, we must work with special substrings—substrings that are themselves special binary strings.

A brute-force approach would involve generating all possible decompositions into special substrings, permuting them, and checking which concatenation is lexicographically largest. However, this quickly becomes infeasible as the string grows.

The key insight is that the structure of special binary strings is recursive: any special string can be broken down into a list of special substrings. If we sort these substrings in descending lexicographical order and then recursively apply this process within each substring, we can always build the largest possible string.

This recursive, greedy approach leverages the problem's constraints and properties, avoiding the need for brute-force permutation.

Solution Approach

  • 1. Decompose:
    • Iterate through the string, tracking the balance between '1's and '0's.
    • Whenever the balance returns to zero, we've found a special substring.
  • 2. Recursion:
    • For each special substring found, recursively process its inner part (excluding the first and last character, which are always '1' and '0' respectively in a special substring).
  • 3. Sort:
    • After all substrings are processed, sort them in descending lexicographical order.
  • 4. Concatenate:
    • Join the sorted substrings to form the result.

This approach ensures that at every level of the recursive decomposition, the largest substrings are placed first, yielding the lexicographically greatest result.

Example Walkthrough

Let's take the input s = "11011000".

  • Step 1: Decompose
    • Scan from left to right, track balance: +1 for '1', -1 for '0'.
    • At index 3, balance is 0: substring 1100 (from 0 to 3) is special.
    • Continue, at index 7, balance is 0: substring 11011000 (from 0 to 7) is special.
    • But inside 11011000, after the first 1100, we have 1000 (from 4 to 7), which is also special.
  • Step 2: Recursion
    • Process 1100: its inner part is 10, which is special but cannot be decomposed further.
    • Process 1000: its inner part is 00, which is not special (unequal count), so we stop.
  • Step 3: Sort
    • Sort the decomposed substrings: 1100 and 1001 (if present), in descending order.
  • Step 4: Concatenate
    • Combine sorted substrings to form the final result: 11100100.

Thus, 11011000 becomes 11100100 after rearrangement.

Code Implementation

class Solution:
    def makeLargestSpecial(self, s: str) -> str:
        subs = []
        count = i = 0
        for j, ch in enumerate(s):
            count += 1 if ch == '1' else -1
            if count == 0:
                # Recursively process the inner part
                subs.append('1' + self.makeLargestSpecial(s[i+1:j]) + '0')
                i = j + 1
        subs.sort(reverse=True)
        return ''.join(subs)
      
class Solution {
public:
    string makeLargestSpecial(string s) {
        vector<string> subs;
        int count = 0, i = 0;
        for (int j = 0; j < s.size(); ++j) {
            count += (s[j] == '1') ? 1 : -1;
            if (count == 0) {
                subs.push_back("1" + makeLargestSpecial(s.substr(i + 1, j - i - 1)) + "0");
                i = j + 1;
            }
        }
        sort(subs.rbegin(), subs.rend());
        string result;
        for (auto &sub : subs) result += sub;
        return result;
    }
};
      
class Solution {
    public String makeLargestSpecial(String s) {
        List<String> subs = new ArrayList<>();
        int count = 0, i = 0;
        for (int j = 0; j < s.length(); ++j) {
            count += s.charAt(j) == '1' ? 1 : -1;
            if (count == 0) {
                subs.add("1" + makeLargestSpecial(s.substring(i + 1, j)) + "0");
                i = j + 1;
            }
        }
        Collections.sort(subs, Collections.reverseOrder());
        StringBuilder sb = new StringBuilder();
        for (String sub : subs) sb.append(sub);
        return sb.toString();
    }
}
      
var makeLargestSpecial = function(s) {
    let subs = [];
    let count = 0, i = 0;
    for (let j = 0; j < s.length; ++j) {
        count += s[j] === '1' ? 1 : -1;
        if (count === 0) {
            subs.push('1' + makeLargestSpecial(s.slice(i + 1, j)) + '0');
            i = j + 1;
        }
    }
    subs.sort((a, b) => b.localeCompare(a));
    return subs.join('');
};
      

Time and Space Complexity

Brute-force approach: Generating all possible decompositions and permutations would have factorial complexity, specifically O(n!), which is infeasible for strings longer than a few characters.

Optimized recursive approach: Each level of recursion processes each character once, and at each step, substrings are sorted. The worst-case time complexity is O(n2 log n), where n is the length of the string: O(n) for scanning and splitting, and O(k log k) for sorting k substrings at each recursion level. Since the depth of recursion is at most O(n), the overall complexity is acceptable for the input constraints.

Space complexity: O(n2) in the worst case, due to recursive stack and the storage of substrings.

Summary

The problem leverages the recursive structure of special binary strings. By decomposing the string into special substrings, recursively maximizing each, and sorting them in descending lexicographical order, we can efficiently build the largest possible special binary string. This approach is both elegant and efficient, sidestepping the need for brute-force permutations by exploiting the unique properties of the input.