Longest Repeating Character Replacement is a classic sliding window problem with a clever twist: the window grows freely and only shrinks when it becomes too expensive to fix.
Problem. Given a string s and an integer k, you may replace at most k characters with any
other uppercase letter. Return the length of the longest substring that can be made up of a single
repeated character after those replacements.
Example: s = "AABABBA", k = 1 → answer 4 (the substring "AABA" becomes "AAAA" by changing its one B).
The slow way first
The obvious idea: try every substring, and for each one count its most frequent character. A substring of length len works if len - maxFreq <= k, because every other character can be swapped to match the most common one. Checking all substrings is O(n²) (or worse), which is too slow for large strings.
The question to ask: as I extend a window to the right, can I avoid re-scanning it every time? Yes — if I keep a running count of characters inside the window, I can decide validity in O(1).
The idea: grow, and only shrink when invalid
Keep a window [L, R] and a count of how often each character appears inside it. Let maxFreq be the highest of those counts. The number of characters we would have to replace is (R - L + 1) - maxFreq.
- If that is
<= k, the window is valid — record its length. - If it exceeds
k, the window is invalid — slideLright (dropping a character) until it is valid again.
The neat part: len - maxFreq is exactly how many characters are not the majority char — and those are precisely the ones we would replace.
Walk through it
Step through the animation. R marches right, adding each character to the count. As long as the window can be fixed with 1 swap it keeps growing, reaching length 4 on "AABA". When adding a fifth character pushes the needed swaps to 2 > k, the L pointer slides right to drop a character and restore validity. The best length ever seen — 4 — is the answer.
Pseudocode
count = empty map
L = 0, best = 0, max_freq = 0
for R from 0 to len(s) - 1:
count[s[R]] += 1
max_freq = max(max_freq, count[s[R]])
while (R - L + 1) - max_freq > k: # window needs too many swaps
count[s[L]] -= 1 # drop the leftmost char
L += 1
best = max(best, R - L + 1) # this window is valid
return bestThe Python solution
def character_replacement(s, k):
from collections import Counter
count = Counter()
L = best = max_freq = 0
for R in range(len(s)):
count[s[R]] += 1
max_freq = max(max_freq, count[s[R]])
while (R - L + 1) - max_freq > k:
count[s[L]] -= 1
L += 1
best = max(best, R - L + 1)
return bestcounttracks how many of each character are inside the current window.max_freqis the count of the window's most common character — the one we keep, replacing all others.(R - L + 1) - max_freqis the number of replacements the window needs. If it exceedsk, thewhileloop shrinks from the left.bestrecords the longest valid window length we have seen.- Subtle but fine:
max_freqis never decreased when we shrink. That can leave it slightly stale, but it can only under-count future windows, sobestis never overstated — and the answer stays correct.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (all substrings) | O(n²) (slow) | recount each substring |
| Sliding window (this solution) | O(n) (moderate) | each index enters and leaves once |
O(1) (fast)L and R each move forward at most n times, so the whole scan is O(n). The count map holds at most 26 uppercase letters, so the extra space is O(1).
When this pattern shows up
Reach for a sliding window whenever a problem asks for the longest or shortest contiguous run that satisfies a constraint. The shape is always: grow the right edge, and shrink the left edge only while the window violates the rule. "Longest substring without repeating characters" and "minimum window substring" are the same move.
The validity test is windowLen - maxFreq <= k, not windowLen <= k. You are counting the characters
you would replace (everything that is not the majority char), not the whole window.
Practice
For s = 'AABABBA', k = 1, the window grows to 'AABA' (length 4). What happens when R extends it to 'AABAB'?
1. What makes a window valid in this problem?
2. Why does the algorithm only shrink the window, never reset it to empty?
3. For s = 'AABABBA', k = 1, what is the answer?
4. What is the extra space used (uppercase letters only)?