LeetCode 1234 - Replace the Substring for Balanced String
The problem asks us to balance a string composed of exactly four types of characters: 'Q', 'W', 'E', and 'R'. A string is balanced when each character occurs exactly n / 4 times, where n is the length of the string.
Difficulty: 🟡 Medium
Topics: String, Sliding Window
Solution
Problem Understanding
The problem asks us to balance a string composed of exactly four types of characters: 'Q', 'W', 'E', and 'R'. A string is balanced when each character occurs exactly n / 4 times, where n is the length of the string. The input is a string s of length n (guaranteed to be a multiple of 4), and the output is the minimum length of a contiguous substring that can be replaced to achieve balance. Replacement can be with any characters of the same length, meaning the substring can be entirely arbitrary, as long as the resulting string becomes balanced.
The constraints 4 <= n <= 10^5 indicate that we need a solution that is at worst linear or linearithmic, as brute-force quadratic solutions will be too slow. Edge cases include strings that are already balanced, strings where one character dominates, or strings where multiple characters exceed their target counts. Since all characters are from a small fixed set, counting occurrences efficiently is feasible with a hash map or fixed-size array.
Approaches
Brute Force Approach: One could consider all possible substrings of s and simulate replacing each substring with characters that would balance the remaining string. For each substring, we would count the occurrences of each character outside that substring and check if replacing the substring could balance the string. While this works in principle, it requires O(n^3) time: O(n^2) for all substrings and O(n) to count the remaining characters for each substring. For n = 10^5, this is infeasible.
Optimal Approach: The key insight is that we only need to replace the excess characters. If a character occurs more than n / 4 times, its extra occurrences must be reduced by replacing a substring containing them. We can use a sliding window to find the smallest substring that contains all excess characters. Outside the window, the counts of all characters must be at most n / 4. We slide the window while maintaining a count of the characters inside it, and for each window, we check if the remaining characters outside are within the target. The minimal window satisfying this condition gives the solution. This is linear time O(n) because each pointer moves at most n steps, and checking counts is constant time since there are only four characters.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3) | O(1) | Check all substrings and simulate replacement; too slow |
| Optimal | O(n) | O(1) | Sliding window containing all excess characters; uses fixed-size hash map |
Algorithm Walkthrough
- Count occurrences: Count the frequency of
'Q','W','E','R'in the strings. Compute the allowed frequencytarget = n / 4. - Identify excess characters: For each character, if its count exceeds
target, record the excess amount; if it does not exceedtarget, treat its excess as zero. - Initialize sliding window: Use two pointers
leftandrightstarting at0.leftwill mark the start of the window, andrightwill expand to include characters. - Expand window: Move
rightthrough the string, decrementing the excess counts for characters as they are included in the window. - Shrink window: Once all excess counts are zero or negative (meaning all excess characters are inside the window), move
leftforward to shrink the window as much as possible while maintaining that condition. - Update minimum: Track the minimal window length found that covers all excess characters.
- Return result: The length of the smallest window found is the minimal substring length that can be replaced to balance the string.
Why it works: By maintaining the invariant that outside the window, no character exceeds its allowed frequency, we ensure that replacing the window with any arbitrary string will make the overall string balanced. The sliding window guarantees minimality because we always attempt to shrink it from the left once the condition is satisfied.
Python Solution
class Solution:
def balancedString(self, s: str) -> int:
from collections import Counter
n = len(s)
target = n // 4
count = Counter(s)
excess = {c: max(0, count[c] - target) for c in 'QWER'}
if all(v == 0 for v in excess.values()):
return 0
min_len = n
left = 0
for right, char in enumerate(s):
if char in excess:
excess[char] -= 1
while left <= right and all(v <= 0 for v in excess.values()):
min_len = min(min_len, right - left + 1)
if s[left] in excess:
excess[s[left]] += 1
left += 1
return min_len
Explanation: We first count character frequencies and determine the excess amounts. If all excesses are zero, the string is already balanced. Otherwise, we expand a window with right while decrementing excess counts. Once the condition is met that all excess counts are zero or negative, we attempt to shrink the window from left, updating the minimal length found. Finally, we return the minimal window length.
Go Solution
func balancedString(s string) int {
n := len(s)
target := n / 4
count := map[byte]int{'Q': 0, 'W': 0, 'E': 0, 'R': 0}
for i := 0; i < n; i++ {
count[s[i]]++
}
excess := map[byte]int{}
allZero := true
for _, c := range []byte{'Q', 'W', 'E', 'R'} {
if count[c] > target {
excess[c] = count[c] - target
allZero = false
} else {
excess[c] = 0
}
}
if allZero {
return 0
}
minLen := n
left := 0
for right := 0; right < n; right++ {
if _, ok := excess[s[right]]; ok {
excess[s[right]]--
}
for left <= right && excess['Q'] <= 0 && excess['W'] <= 0 && excess['E'] <= 0 && excess['R'] <= 0 {
if right-left+1 < minLen {
minLen = right - left + 1
}
if _, ok := excess[s[left]]; ok {
excess[s[left]]++
}
left++
}
}
return minLen
}
Go-specific notes: We use a map of bytes for counts, carefully managing subtraction and addition as the window expands and contracts. The condition excess[...] <= 0 is unrolled for all four characters since Go does not allow a direct all() function. Otherwise, the logic mirrors the Python implementation.
Worked Examples
Example 1: s = "QWER"
| Step | left | right | excess | min_len |
|---|---|---|---|---|
| Init | 0 | 0 | {Q:0,W:0,E:0,R:0} | 4 |
| Check | - | - | all zero | 0 |
Output: 0
Example 2: s = "QQWE"
| Step | left | right | excess | min_len |
|---|---|---|---|---|
| Init | 0 | 0 | {Q:1,W:0,E:0,R:0} | 4 |
| right=0(Q) | 0 | 0 | {Q:0,W:0,E:0,R:0} | 1 |
| Shrink left | 1 | 0 | {Q:1,W:0,E:0,R:0} | 1 |
Output: 1
Example 3: s = "QQQW"
| Step | left | right | excess | min_len |
|---|---|---|---|---|
| Init | 0 | 0 | {Q:2,W:0,E:0,R:0} | 4 |
| right=0(Q) | 0 | 0 | {Q:1,W:0,E:0,R:0} | 4 |
| right=1(Q) | 0 | 1 | {Q:0,W:0,E:0,R:0} | 2 |
| Shrink left | 1 | 1 | {Q:1,W:0,E:0,R:0} | 2 |
Output: 2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each pointer moves at most n steps, counting and checking excess is O(1) per step |
| Space | O(1) | Fixed-size maps for four characters; constant extra space |
The sliding window ensures linear traversal, and since only four character counts are tracked, space does not grow with input size.
Test Cases
# Provided examples
assert Solution().balancedString("QWER") == 0 # already balanced
assert Solution().balancedString("QQWE") == 1 # need to replace 1 Q
assert Solution().balancedString("QQQW")