Problem Walkthrough

Min Swaps Balanced String LeetCode 1963

Cancel matched bracket pairs by scanning left to right while tracking an open count — after all natural matches are removed, the remaining unmatched brackets form a block of closes followed by opens, and the minimum swaps to balance them is ceil(unmatched / 2).

8 min read|

Min Swaps Balanced

LeetCode 1963

Problem Overview

LeetCode 1963 Minimum Number of Swaps to Make the String Balanced gives you a string of only "[" and "]" characters with an equal number of each. You are allowed to swap any two characters in the string any number of times. The goal is to return the minimum number of swaps required to make the string balanced.

A balanced string is one where every prefix has at least as many open brackets as close brackets — equivalently, no "]" ever appears before its matching "[". Because the total counts of "[" and "]" are equal, a balanced string here means the string can be fully matched into valid bracket pairs.

The naive approach of trying all possible swaps is exponential. The key insight is that you do not need to simulate swaps at all — a single greedy scan lets you count unmatched brackets, and the answer follows directly from that count via a simple formula.

  • String of "[" and "]" only, with equal counts of each character
  • Swap any two positions in the string per operation
  • Return the minimum number of swaps to make the string balanced
  • Balanced means every prefix has open count >= close count

Cancel Matched Pairs

Scan the string from left to right while maintaining a counter for unmatched open brackets. When you encounter "[", increment the open counter — this bracket is potentially matchable. When you encounter "]", check if the open counter is greater than zero: if so, decrement it to cancel a matched pair. If open is zero, this "]" has no matching "[" before it and is unmatched.

After the full scan, the unmatched close brackets (the ones we counted) are all to the left of the unmatched open brackets. This is because we cancelled every "[" that could be matched by a preceding or subsequent "]" in natural order. What remains forms a suffix of the form "]]]..[[[" — all unmatched closes then all unmatched opens.

The number of unmatched closes equals the number of unmatched opens (since the total counts are equal). Call this count "unmatched" (= the close count from our scan, which equals the final open count). The problem reduces to: how many swaps does it take to fix a sequence of k unmatched closes followed by k unmatched opens?

💡

After Cancellation You Have "]]]...[[[" Remaining

After cancelling all naturally matched pairs, you are left with "]]]...[[[" — all closes before all opens. Each swap of "][" into "[]" fixes two unmatched pairs simultaneously. So the answer is ceil(unmatched / 2), where unmatched is the count of remaining unmatched close brackets (which equals the count of remaining unmatched open brackets).

Count Unmatched Brackets

The counting algorithm uses just two integer variables: open and close, both starting at zero. Iterate through every character in the string. When the character is "[", increment open. When the character is "]" and open is greater than zero, decrement open (a match was made). When the character is "]" and open is zero, increment close (this close bracket is unmatched).

At the end of the scan, open and close will be equal (because the input guarantees equal total counts of "[" and "]"). The variable close holds the number of unmatched close brackets, which is also the number of unmatched open brackets. This is the value we use in the formula.

The entire counting step runs in O(n) time and O(1) space — no stack, no auxiliary array, no sorting. Just two counters updated in a single left-to-right pass over the string.

  1. 1open = 0, close = 0
  2. 2For each char in s:
  3. 3 If char == "[": open++
  4. 4 If char == "]" and open > 0: open-- (cancel matched pair)
  5. 5 If char == "]" and open == 0: close++ (unmatched close)
  6. 6After loop: unmatched = close (equals open by guarantee)
  7. 7Answer = (close + 1) // 2

Why ceil(unmatched / 2)

After cancellation, the unmatched portion looks like: "]]]..[[[" with k unmatched closes followed by k unmatched opens. The leftmost "]" and rightmost "[" are the perfect candidates for a swap — swapping them produces "[[...]]" which fixes one "]" that was too early and one "[" that was too late, simultaneously resolving two unmatched brackets at once.

Each such swap reduces the unmatched count by exactly 2 — it converts one unmatched "]" into "[" (now a valid open before many closes) and one unmatched "[" into "]" (now a valid close after many opens). This means k pairs of unmatched brackets can be fixed with k/2 swaps if k is even, or (k+1)/2 swaps if k is odd (the last swap fixes only one remaining unmatched pair when k is odd, though it still only takes one swap).

The formula (unmatched + 1) // 2 in integer arithmetic gives ceil(unmatched / 2). This is the minimum — no strategy can do better because each swap can resolve at most 2 unmatched brackets, so you need at least ceil(k/2) swaps to eliminate k unmatched brackets.

ℹ️

One Swap Converts "][" to "[]" Fixing Two Unmatched

One swap converts "][" to "[]": this fixes both one unmatched "]" (it becomes "[", a valid open) and one unmatched "[" (it becomes "]", a valid close) simultaneously. Hence each swap resolves 2 unmatched brackets. With k unmatched brackets total, the minimum swaps needed is ceil(k / 2) = (k + 1) // 2.

Walk-Through Example

Consider s = "][][". Trace the cancellation pass: index 0 is "]", open=0 so close=1; index 1 is "[", open=1; index 2 is "]", open>0 so open=0 (cancel); index 3 is "[", open=1. End state: open=1, close=1. Unmatched = 1 (one unmatched close and one unmatched open remain).

The answer is ceil(1 / 2) = 1 swap. Verify: swap index 0 ("]") with index 3 ("["): we get "[][][" — wait, s has length 4. Swapping positions 0 and 3 turns "][][" into "[]][" — that is still not balanced. Let us swap positions 0 and 1 instead: "[][]" — balanced in 1 swap. Answer confirmed = 1.

Now consider s = "]][[". Trace: index 0 "]" → close=1; index 1 "]" → close=2; index 2 "[" → open=1; index 3 "[" → open=2. End state: open=2, close=2. Unmatched=2. Answer = ceil(2/2) = 1 swap. Swap index 0 and index 3: "[[]]" — balanced in 1 swap. Confirmed.

  1. 1s = "][][": open=0, close=0
  2. 2Index 0 "]": open==0 → close=1
  3. 3Index 1 "[": open=1
  4. 4Index 2 "]": open>0 → open=0 (cancel)
  5. 5Index 3 "[": open=1
  6. 6End: close=1, open=1 → unmatched=1
  7. 7Answer = (1 + 1) // 2 = 1 swap
  8. 8Swap index 0 and 1: "[][]" — balanced, confirmed

Code Walkthrough — Python and Java

Python solution: def minSwaps(s: str) -> int: open = 0; close = 0; for c in s: if c == "[": open += 1; elif open > 0: open -= 1; else: close += 1; return (close + 1) // 2. The loop runs in O(n) and the two variables give O(1) space. The return expression uses integer division to compute ceil(close / 2).

Java solution: public int minSwaps(String s) { int open = 0, close = 0; for (char c : s.toCharArray()) { if (c == "[") open++; else if (open > 0) open--; else close++; } return (close + 1) / 2; } This is identical logic in Java. The (close + 1) / 2 expression performs integer floor division which equals ceil(close / 2) for non-negative integers.

Both solutions are O(n) time and O(1) space. The only difference from a naive stack-based solution is that we use a counter instead of an actual stack — since we only ever need to know whether the stack is empty (open > 0) and what its size is (the open counter), no actual stack data structure is necessary.

⚠️

Do Not Use a Stack — Use a Counter Instead

Don't use an actual stack to track unmatched brackets: it works correctly but wastes O(n) space. Since you only need to know if the stack is non-empty and how many items are in it, a single integer counter for open brackets gives identical results in O(1) space. The counter approach is both simpler and more memory-efficient.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now