Rearrange Letters So No Two Neighbors Match
Solve this ProblemRearrange the letters of a string so that no two equal letters are next to each other. Because many arrangements can be valid, this problem asks for one specific arrangement — the one built greedily: at every position, take the letter with the most unused copies among the letters different from the one just placed, and break ties toward the alphabetically smaller letter. If at some position nothing different is available, no valid arrangement exists and the answer is the empty string.
A max-heap of (copies, letter) with a one-step cool-down for the letter just placed carries out that rule in O(log 26) per position, instead of scanning all 26 letters each time.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ s.length ≤ 30, s contains only lowercase English letters - ◆
Build the result one character at a time. At every position, append the letter that has the MOST copies still unused among the letters different from the one just appended; if several letters tie, append the alphabetically smaller one. - ◆
If at some position no letter different from the previous one has copies left, no valid arrangement exists — return the empty string "" - ◆
Otherwise return the arrangement built by that rule (it always has no two equal neighbors)
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Scan All 26 Letters at Every Position
BruteKeep a count of unused copies per letter. For each position, scan the whole alphabet: skip the letter just used and any letter with no copies left, and among the rest pick the one with the most copies (the first such letter in alphabetical order wins ties, because only a strictly larger count replaces the current best). If the scan finds nothing, no arrangement is possible. It is correct, but every single position pays for a full 26-letter scan.
O(26 · n)O(1) extra1class Solution {
2 public String rearrangeLetters(String s) {
3 int[] counts = new int[26];
4 for (char ch : s.toCharArray()) counts[ch - 'a']++;
5 StringBuilder result = new StringBuilder();
6 int previous = -1;
7 for (int position = 0; position < s.length(); position++) {
8 int best = -1;
9 for (int letter = 0; letter < 26; letter++) {
10 if (letter == previous || counts[letter] == 0) continue;
11 if (best == -1 || counts[letter] > counts[best]) best = letter;
12 }
13 if (best == -1) return "";
14 result.append((char) ('a' + best));
15 counts[best]--;
16 previous = best;
17 }
18 return result.toString();
19 }
20}Optimal — Max-Heap With a One-Step Cool-Down
OptimalPut every letter with copies left into a max-heap ordered by (most copies first, then alphabetical). Repeatedly pop the top letter and append it, then use up one copy. The letter just used must not be chosen next, so it is held out of the heap for exactly one step and returned to the heap (if it still has copies) right after the following letter is popped. Each step costs O(log 26) heap work instead of a 26-letter scan. If the heap runs dry while the held letter still has copies, the arrangement is impossible and the result is shorter than the input.
O(n log 26)O(1) extra1class Solution {
2 public String rearrangeLetters(String s) {
3 int[] counts = new int[26];
4 for (char ch : s.toCharArray()) counts[ch - 'a']++;
5 PriorityQueue<int[]> heap = new PriorityQueue<>(
6 (a, b) -> a[0] != b[0] ? b[0] - a[0] : a[1] - b[1]);
7 for (int letter = 0; letter < 26; letter++) {
8 if (counts[letter] > 0) heap.offer(new int[]{counts[letter], letter});
9 }
10 StringBuilder result = new StringBuilder();
11 int[] held = null;
12 while (!heap.isEmpty()) {
13 int[] current = heap.poll();
14 result.append((char) ('a' + current[1]));
15 current[0]--;
16 if (held != null && held[0] > 0) heap.offer(held);
17 held = current;
18 }
19 return result.length() == s.length() ? result.toString() : "";
20 }
21}