GreedyProblem 34 of 35
Rearrange characters in a string such that no two adjacent are same
Problem Statement
Given a string with repeated characters, rearrange the characters such that no two adjacent characters are the same. If not possible, return an empty string.
Constraints:
- 1 ≤ length ≤ 10^5
- String contains lowercase English letters
Example:
- Input:
s = "aab" - Output:
"aba"or"baa"... wait, "baa" has adjacent 'a's. Output:"aba"
Example 2:
- Input:
s = "aaab" - Output:
""(impossible - 3 a's can't be separated with only 1 b)
Example 3:
- Input:
s = "aabb" - Output:
"abab"or"baba"
Approach 1: Brute Force (Try All Permutations)
Intuition
Generate all permutations of the string and find one where no two adjacent characters are the same.
Algorithm
- Generate all permutations of the string
- For each permutation, check if valid (no adjacent same characters)
- Return first valid permutation or empty string if none exists
java
import java.util.*;
public class Solution {
private boolean isValid(String s) {
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == s.charAt(i-1)) return false;
}
return true;
}
public String rearrangeStringBrute(String s) {
char[] chars = s.toCharArray();
Arrays.sort(chars);
return permute(chars, 0);
}
private String permute(char[] chars, int start) {
if (start == chars.length) {
String s = new String(chars);
return isValid(s) ? s : null;
}
for (int i = start; i < chars.length; i++) {
swap(chars, start, i);
String result = permute(chars, start + 1);
if (result != null) return result;
swap(chars, start, i);
}
return null;
}
private void swap(char[] chars, int i, int j) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}Complexity Analysis
- Time Complexity: O(n!) - All permutations
- Space Complexity: O(n) - For storing permutation
Approach 2: Greedy with Max-Heap (Optimal)
Intuition
Always place the character with the highest remaining frequency, but not the same as the last placed character. Use a max-heap to efficiently get the most frequent character.
Key Insight:
A valid arrangement exists if and only if no character appears more than (n+1)/2 times.
Algorithm
- Count frequency of each character
- Check if any character exceeds
(n+1)/2- if yes, return "" - Use max-heap based on frequency
- Always pick the most frequent character different from the last placed
- Build result string
java
import java.util.*;
public class Solution {
public String rearrangeString(String s) {
// Count frequencies
Map<Character, Integer> freq = new HashMap<>();
for (char c : s.toCharArray()) {
freq.put(c, freq.getOrDefault(c, 0) + 1);
}
// Check if valid
int n = s.length();
for (int count : freq.values()) {
if (count > (n + 1) / 2) {
return "";
}
}
// Max-heap based on frequency
PriorityQueue<int[]> maxHeap = new PriorityQueue<>(
(a, b) -> b[0] - a[0]
);
for (Map.Entry<Character, Integer> e : freq.entrySet()) {
maxHeap.offer(new int[]{e.getValue(), e.getKey()});
}
StringBuilder result = new StringBuilder();
int[] prev = {0, '#'};
while (!maxHeap.isEmpty()) {
int[] curr = maxHeap.poll();
result.append((char)curr[1]);
if (prev[0] > 0) {
maxHeap.offer(prev);
}
prev = new int[]{curr[0] - 1, curr[1]};
}
return result.toString();
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.rearrangeString("aab")); // Output: "aba"
System.out.println(sol.rearrangeString("aaab")); // Output: ""
System.out.println(sol.rearrangeString("aabb")); // Output: "abab"
}
}Dry Run Example
s = "aaabbc"
n = 6, max allowed frequency = (6+1)/2 = 3
Frequencies: a=3, b=2, c=1
Max frequency = 3 ≤ 3, so valid arrangement exists
Max-heap: [(3,a), (2,b), (1,c)]
result = "", prev = (0, #)
Iteration 1:
- Pop (3,a), result = "a"
- prev (0,#) has count 0, don't push
- prev = (2,a)
- heap = [(2,b), (1,c)]
Iteration 2:
- Pop (2,b), result = "ab"
- Push prev (2,a) to heap
- prev = (1,b)
- heap = [(2,a), (1,c)]
Iteration 3:
- Pop (2,a), result = "aba"
- Push prev (1,b) to heap
- prev = (1,a)
- heap = [(1,b), (1,c)]
Iteration 4:
- Pop (1,b), result = "abab"
- Push prev (1,a) to heap
- prev = (0,b)
- heap = [(1,a), (1,c)]
Iteration 5:
- Pop (1,a), result = "ababa"
- prev (0,b) has count 0, don't push
- prev = (0,a)
- heap = [(1,c)]
Iteration 6:
- Pop (1,c), result = "ababac"
- prev (0,a) has count 0, don't push
- prev = (0,c)
- heap = []
Result: "ababac"
Verification: No adjacent characters are same ✓
Complexity Analysis
- Time Complexity: O(n log n) - n heap operations, each O(log 26) ≈ O(log n)
- Space Complexity: O(n) - For result string and heap
Approach 3: Fill Even and Odd Positions
Intuition
Place the most frequent character at even indices first, then fill remaining positions.
Edge Cases
Key Takeaways
- Validity Condition: No character should appear more than
(n+1)/2times - Greedy Choice: Always place the most frequent character (if different from last)
- Max-Heap: Efficiently tracks the most frequent available character
- Alternate Strategy: Fill even positions first, then odd positions
- Proof of Correctness: If valid, greedy always produces a valid arrangement
- Similar Problems: Task scheduler, reorganize string with k distance
- LeetCode Reference: LeetCode 767 - Reorganize String