Search lands in PR-5.1 (Pagefind).

How-to Advanced

Chapter 4 Updated

Reorganize String

Max-heap by frequency; always emit top, defer the one used last.

  • Full 7m
  • Revision
  • Flow

Problem Statement

Given a string s, return any permutation such that no two adjacent characters are equal. If impossible, return the empty string.

Input

A lowercase string s.

Example input
s = "aaabbc"

Output

Any valid rearrangement, or "" if none exists.

Expected output
"ababac"   // or "abacab" — any valid permutation

Constraints

  • 1 ≤ s.length ≤ 500
  • s consists of lowercase English letters.

Intuition

Greedy: always place the character with the highest remaining count — unless it’s the same as the previous one, in which case place the next-best. Use a max-heap keyed on frequency. Pop the top, append to output, decrement, and don’t push it back immediately — stash it as “just used”. On the next iteration, pop the new top, then push the stash back in. This guarantees no two consecutive picks are the same character.

Feasibility check: if any character has frequency greater than ⌈n / 2⌉, no valid arrangement exists.

Walkthrough — s='aaabbc'
freq: {a:3, b:2, c:1}    max_freq=3 ≤ ⌈6/2⌉=3   → feasible
 
heap (max): [(3,a),(2,b),(1,c)]    prev = (0, "")
pop (3,a) → emit 'a' → prev=(2,a)   heap=[(2,b),(1,c)]
pop (2,b) → emit 'b' → push prev → prev=(1,b)   heap=[(2,a),(1,c)]
pop (2,a) → emit 'a' → push prev → prev=(1,a)   heap=[(1,b),(1,c)]
pop (1,b) → emit 'b' → push prev → prev=(0,b)   heap=[(1,a),(1,c)]  // prev_cnt==0 → drop
pop (1,a) → emit 'a' → push prev (0) dropped → prev=(0,a)   heap=[(1,c)]
pop (1,c) → emit 'c' → prev=(0,c)   heap=[]
 
result = "ababac" ✓

Solution

reorganize_string.py
import heapq
from collections import Counter
 
def reorganizeString(s: str) -> str:
    freq = Counter(s)
    if max(freq.values()) > (len(s) + 1) // 2:
        return ""                                   # infeasible
 
    max_heap = [(-cnt, ch) for ch, cnt in freq.items()]
    heapq.heapify(max_heap)
 
    result: list[str] = []
    prev_cnt, prev_ch = 0, ""                       # "just used" stash
 
    while max_heap:
        cnt, ch = heapq.heappop(max_heap)           # cnt is negative
        result.append(ch)
        if prev_cnt < 0:
            heapq.heappush(max_heap, (prev_cnt, prev_ch))
        prev_cnt, prev_ch = cnt + 1, ch             # increment toward 0
 
    return "".join(result)

Complexity

MetricValueReason
TimeO(n log u)Each of n characters requires a pop (and maybe a push): log u each, with u = distinct characters. For ASCII, u ≤ 26 — heap ops effectively constant.
SpaceO(u)Map + heap hold at most u entries. Output is O(n) — required, not auxiliary.

Edge Case Thinking

ScenarioHandling
Infeasibility"aaab"a has count 3 > ⌈4/2⌉ = 2, return "". Check must run upfront.
Single character stringTrivially valid; return s.
Empty inputReturn empty string.
All distinctAny permutation works; heap just pops them all.
Exactly at threshold (max_freq == ⌈n/2⌉)Still feasible but tight; the most-frequent char must lead and alternate perfectly.

Comments

Comments are disabled in this environment. Set PUBLIC_GISCUS_REPO, PUBLIC_GISCUS_REPO_ID, and PUBLIC_GISCUS_CATEGORY_ID to enable.