Rearrange String k Distance Apart
Master this topic with zero to advance depth.
Rearrange String k Distance Apart
Given a string s and an integer k, rearrange s such that the same characters are at least distance k from each other. If it is not possible to rearrange the string, return an empty string "".
Visual Representation
s = "aabbcc", k = 3
Frequency Count:
a: 2, b: 2, c: 2
Step-by-Step Selection (k=3):
Pos 0: Use 'a' (freq 2->1) | Wait: {a: wait 2}
Pos 1: Use 'b' (freq 2->1) | Wait: {a: wait 1, b: wait 2}
Pos 2: Use 'c' (freq 2->1) | Wait: {a: wait 0, b: wait 1, c: wait 2}
Pos 3: 'a' is free! Use 'a'| Wait: {b: wait 0, c: wait 1, a: wait 2}
...
Result: "abcabc"Examples
Level I: Greedy with Max-Heap (Standard)
Intuition
We should always try to place the most frequent characters as early as possible. However, once a character is used, it cannot be used again for the next k-1 positions. Use a Max-Heap to track frequencies and a queue to manage the 'waitlist'.
Thought Process
- Count frequency of each character.
- Push char-frequency pairs into a Max-Heap.
- While heap is not empty:
- Extract the character with the max frequency.
- Append it to the result.
- Decrement its frequency.
- Add it to a 'waitlist' queue.
- If the waitlist queue size reaches
k, pop the character from the front and push it back into the heap (if its freq > 0).
Detailed Dry Run
s = "aaabc", k = 3
- Map: {a:3, b:1, c:1}, Heap: [a:3, b:1, c:1], Wait: []
- Step 1: Use 'a'. Result: "a", Heap: [b:1, c:1], Wait: [(a, 2)]
- Step 2: Use 'b'. Result: "ab", Heap: [c:1], Wait: [(a, 2), (b, 0)]
- Step 3: Use 'c'. Result: "abc", Heap: [], Wait: [(a, 2), (b, 0), (c, 0)]
- Wait size 3: Push 'a' back to heap. Heap: [a:2]
- Continue... (If result length != s length, return "")
Level II: Greedy with Alphabet Array (O(N * 26))
Intuition
Instead of a Max-Heap, we can directly use a frequency array of size 26. At each position in the result string, we scan all 26 characters to find the one that: 1. Is allowed to be placed (distance since last use >= k). 2. Has the maximum remaining frequency.
Detailed Dry Run
s = "aabbcc", k = 3
- Freqs: {a:2, b:2, c:2}, LastPos: {a:-inf, b:-inf, c:-inf}
- Pos 0: 'a' is valid and max freq. Res: "a", Freqs: {a:1...}, LastPos: {a:0}
- Pos 1: 'a' invalid (1 < 3), 'b' is valid and max freq. Res: "ab"...
- Pos 3: 'a' becomes valid again (3-0 >= 3).
Level III: Optimal (Modified Greedy with Max-Freq Limit Check)
Intuition
This approach uses a Max-Heap but adds a check to see if it's mathematically possible to rearrange. If the most frequent character appears more times than the available slots given distance k, it's impossible. We use a waitlist (cooling queue) to manage the distance k requirement.
Detailed Dry Run
s = "aaabc", k = 3
- Freqs: {a:3, b:1, c:1}. Heap: [a:3, b:1, c:1]. Wait: []
- Pop 'a'. Res: "a". Freq: a:2. Wait: [a:2].
- Wait size < 3. Continue.
- If heap empty and res.length < s.length, return "".
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.