String · 30 of 43
Minimum number of swaps for bracket balancing
Count how many times neighbors must trade places so every closing bracket has an opening bracket before it.
- Simple way
- time O(n^2)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a string with the same number of [ and ]. One swap trades two brackets that stand next to each other. Find the fewest swaps that make the string balanced: reading left to right, you never have more ] than [ so far.
For example []][][ needs 2 swaps to become [][][].
A story to remember it
Picture it
[]]][[: the first pair is fine. Then two ] have no [ in front of them. Those two are waiting.
- done
- rejected
Examples
A normal case
- In
s = '[]][]['- Out
2
Swap the third and fourth brackets to get [][]][, then the last two to get [][][].
Already balanced
- In
s = '[[][]]'- Out
0
No ] ever comes before its partner, so no swaps.
Fully backwards
- In
s = ']][['- Out
3
The first [ must move 2 places forward and the second must move 1. That is 3 swaps to reach [][].
One wrong pair
- In
s = ']['- Out
1
One swap gives [].
Way 1: the simple way
Do the swaps for real. Walk the string with a counter of spare tickets. When you meet a ] with no spare ticket, find the next [ to the right and bubble it back, one neighbor swap at a time, until it stands where the ] was.
- Keep
depth: how many[are still open. - On
[, add 1. On]withdepth > 0, subtract 1. - On
]withdepth == 0, find the next[atj, swap it back toi, and addj - iswaps. Now positioniholds a[, sodepthbecomes 1.
class Solution {
public int minSwaps(String s) {
char[] a = s.toCharArray();
int swaps = 0, depth = 0;
for (int i = 0; i < a.length; i++) {
if (a[i] == '[') {
depth++;
} else if (depth > 0) {
depth--;
} else {
int j = i + 1;
while (a[j] != '[') j++; // the next ticket kid
while (j > i) { // walk it forward, one trade at a time
char t = a[j]; a[j] = a[j - 1]; a[j - 1] = t;
j--;
swaps++;
}
depth = 1; // a[i] is now '['
}
}
return swaps;
}
}How long it takes: each bubble can walk across most of the string, so O(n^2). Extra room: the character array, O(n).
Way 2: the clever way
We never need to move anything. When a [ arrives, the number of swaps it needs is the number of ] that are waiting before it. So keep that number, the imbalance.
- Count
open(how many[so far) andclose(how many]so far). - On
]:close++, and setimbalance = close - open. If it is above 0, that many]are waiting. - On
[:open++. Ifimbalance > 0, addimbalanceto the swaps (it walks past all of them), thenimbalance--(one of them got its partner).
Counting swaps for []]][[
01 / 07
[ has a ticket. Nobody is waiting, so it just walks on. Open tickets: 1.
- swaps
- 0
- looking now
- seen
- done
- rejected
- just added
class Solution {
public int minSwaps(String s) {
int open = 0, close = 0, imbalance = 0, swaps = 0;
for (char c : s.toCharArray()) {
if (c == '[') {
open++;
if (imbalance > 0) {
swaps += imbalance; // walk past every waiting ]
imbalance--; // one of them gets its partner
}
} else {
close++;
imbalance = close - open; // how many ] are waiting now
}
}
return swaps;
}
}How long it takes: one walk, O(n). Extra room: four numbers, O(1).
Watch out for
- Take the
imbalancebefore you lower it. The[walks past all the waiting], including the one it then pairs with. - On
], recomputeimbalance = close - openinstead of adding 1. It may be 0 or below when there were spare[. - The problem promises equal counts of
[and]. If not, it can never be balanced, so check counts first if the input is not trusted.
What interviewers ask next
- Any two brackets may be swapped? Count the most lonely
]at any moment (k) and return(k + 1) / 2. One swap fixes two of them. - Why is this the minimum? Each
]that is waiting must have some[cross over it, and every neighbor swap moves one[past one]. The greedy counts exactly those crossings, no more. - Flips instead of swaps, with
{and}? That is "Minimum number of bracket reversals":ceil(close / 2) + ceil(open / 2). - Return the final string? It is always the same: follow the counts, or run Way 1 if
nis small.
Remember this
Grown-up words: the waiting kids are the imbalance, the trades are adjacent swaps, and moving each [ as early as needed is a greedy choice that counts the inversions between brackets.