Longest Repeating Character Replacement in Java DSA
-
Last Updated: August 25, 2026
-
By: javahandson
-
Series

Solve Longest Repeating Character Replacement in Java DSA with a clear sliding window. Full step-by-step dry runs, simple pseudocode, and easy Java code for beginners.
Longest Repeating Character Replacement in Java is a problem that feels tricky at first, but it clicks once you see the idea. You get a string and a number k. You may change up to k characters to any letter you want. Your job is to find the longest run where all letters become the same after those changes.
Let us make that real. Say the string is “AABABBA” and k is 1. You are allowed to swap one letter. The best you can do is turn one letter and get a block of four same letters. So the answer is 4.
The catch is that you do not actually change the string. You only count. For any window of letters, you check one thing: how many letters would you need to change to make them all equal? That number is the window size minus the count of its most common letter. If that number is k or less, the window is valid.
We will solve it in two ways. First a brute force that checks every window, which is slow but easy to trust. Then a sliding window that scans the string once and stays fast. Both get a full, step-by-step dry run on the same seven-letter string, so you can watch every count change and see why the answer lands at 4.
Let us fix the rules before any code.
For our pair the answer is 4. Inside “AABABBA” the window “AABA” has three A’s and one B. Change that one B to an A and you get four A’s in a row. That uses only one change, which fits k = 1.
Take any window of letters. To make them all equal, keep the most common letter and change the rest. So the number of changes you need is simple.
replacements needed = window length - count of most common letter
A window is valid when that number is k or less. This one line is the heart of the whole problem.
The input here is uppercase English letters, so we can count with a small int array of 26 slots. Slot 0 is A, slot 1 is B, and so on. To turn a letter into its slot we use ch minus ‘A’.
The plain idea is to try every possible window. For each start index, grow the window one letter at a time. For each window, count the letters, find the most common one, and check if the replacements needed fit within k. Track the longest valid window as you go.
best = 0
for start in 0 .. n-1:
count = array of 26 zeros
for end in start .. n-1:
count[s[end] - 'A'] += 1
windowLen = end - start + 1
maxFreq = largest value in count
if windowLen - maxFreq <= k:
best = max(best, windowLen)
return bestWe test every window by picking a start and stretching the end.
public class LongestRepeatingBrute {
public static int characterReplacement(String s, int k) {
int n = s.length();
int best = 0;
for (int start = 0; start < n; start++) {
int[] count = new int[26];
for (int end = start; end < n; end++) {
count[s.charAt(end) - 'A']++;
int windowLen = end - start + 1;
int maxFreq = 0;
for (int c : count) {
maxFreq = Math.max(maxFreq, c);
}
if (windowLen - maxFreq <= k) {
best = Math.max(best, windowLen);
}
}
}
return best;
}
public static void main(String[] args) {
System.out.println(characterReplacement("AABABBA", 1)); // 4
}
}This is the same plan in Java. It uses two loops to build every window and a small inner loop to find the most common letter.
Let us trace every window that starts at index 0, using s = “AABABBA” and k = 1. The string has 7 letters. We grow the end one step at a time and check each window.
| end | letter added | window | counts | maxFreq | windowLen – maxFreq | valid? (<= 1) |
|---|---|---|---|---|---|---|
| 0 | A | A | A:1 | 1 | 1 – 1 = 0 | Yes, best = 1 |
| 1 | A | AA | A:2 | 2 | 2 – 2 = 0 | Yes, best = 2 |
| 2 | B | AAB | A:2, B:1 | 2 | 3 – 2 = 1 | Yes, best = 3 |
| 3 | A | AABA | A:3, B:1 | 3 | 4 – 3 = 1 | Yes, best = 4 |
| 4 | B | AABAB | A:3, B:2 | 3 | 5 – 3 = 2 | No |
| 5 | B | AABABB | A:3, B:3 | 3 | 6 – 3 = 3 | No |
| 6 | A | AABABBA | A:4, B:3 | 4 | 7 – 4 = 3 | No |
So the start = 0 pass already finds a valid window of length 4. The brute force keeps going with start = 1, start = 2, and so on, but no window ever beats 4. For example, start = 2 finds the window “BABB” (three B’s, one A) which is also length 4 and valid, but it just ties.
Let us walk the start = 0 pass step by step, because it is where the answer shows up.
end = 0, add A.
end = 1, add A.
end = 2, add B.
end = 3, add A.
end = 4, add B.
end = 5, add B.
end = 6, add A.
Notice how the changes needed grew as the window got longer. Early on, one B was cheap to fix. Later, too many odd letters piled up and broke the limit. The brute force found the answer, but it recounted the whole window from scratch every time, which is wasted effort.
💡 Interview Insight If asked why brute force is slow here, point to the inner loop that scans all 26 slots for maxFreq on every single window. That extra work stacks up and pushes the time cost higher.
The brute force is easy to trust, but it redoes the same counting again and again. Next we fix that with a sliding window that never looks back.
The smart idea is to keep one window and slide it across the string. We use two pointers, lower and higher, that mark the window edges. We move higher forward to add letters. When the window needs more than k changes, we move lower forward to drop a letter and shrink it. That way each letter is visited a small, fixed number of times.
count = array of 26 zeros
lower = 0
maxFreq = 0
best = 0
for higher in 0 .. n-1:
count[s[higher] - 'A'] += 1
maxFreq = max(maxFreq, count[s[higher] - 'A'])
while (higher - lower + 1) - maxFreq > k:
count[s[lower] - 'A'] -= 1
lower += 1
best = max(best, higher - lower + 1)
return bestThink of the window as a stretchy band. The right edge keeps growing. The left edge only moves when the window breaks the rule.
One thing surprises people: we never lower maxFreq when we shrink. That is fine. The best answer can only grow, so a slightly stale maxFreq never causes a wrong result. It just keeps the code simple and fast.
public class LongestRepeatingSliding {
public static int characterReplacement(String s, int k) {
int[] count = new int[26];
int lower = 0;
int maxFreq = 0;
int best = 0;
for (int higher = 0; higher < s.length(); higher++) {
int idx = s.charAt(higher) - 'A';
count[idx]++;
maxFreq = Math.max(maxFreq, count[idx]);
while ((higher - lower + 1) - maxFreq > k) {
count[s.charAt(lower) - 'A']--;
lower++;
}
best = Math.max(best, higher - lower + 1);
}
return best;
}
public static void main(String[] args) {
System.out.println(characterReplacement("AABABBA", 1)); // 4
}
}The code is short because one loop does everything. The higher pointer is the loop variable and lower slides only when it has to.
Let us trace the whole run on s = “AABABBA” and k = 1. The window starts empty with lower = 0. Each row shows what happens after higher moves one step. When the window breaks the rule, the shrink column shows the letter we drop and where lower lands.
Legend: higher is the right edge, lower is the left edge, window is higher minus lower plus 1, and need is window minus maxFreq.
| higher | letter | count after add | maxFreq | window | need (window – maxFreq) | shrink? | best |
|---|---|---|---|---|---|---|---|
| 0 | A | A:1 | 1 | 1 | 1 – 1 = 0 | no | 1 |
| 1 | A | A:2 | 2 | 2 | 2 – 2 = 0 | no | 2 |
| 2 | B | A:2, B:1 | 2 | 3 | 3 – 2 = 1 | no | 3 |
| 3 | A | A:3, B:1 | 3 | 4 | 4 – 3 = 1 | no | 4 |
| 4 | B | A:3, B:2 | 3 | 5 | 5 – 3 = 2 | yes: drop A, lower -> 1 | 4 |
| 5 | B | A:2, B:3 | 3 | 5 | 5 – 3 = 2 | yes: drop A, lower -> 2 | 4 |
| 6 | A | A:2, B:3 | 3 | 5 | 5 – 3 = 2 | yes: drop B, lower -> 3 | 4 |
The counts in the last three rows show the window after the shrink. Before each shrink the window was size 5, which needed 2 changes and broke the k = 1 rule. After dropping one letter the window is back to size 4, which needs only 1 change and is valid again.
Let us go step by step and watch the window grow, then hold steady at size 4.
higher = 0, add A.
higher = 1, add A.
higher = 2, add B.
higher = 3, add A.
higher = 4, add B.
higher = 5, add B.
higher = 6, add A.
See the pattern in the second half. Once best reached 4, the window never shrank below size 4 and never grew past it. Each time higher added a letter that made the window too costly, lower slid forward by exactly one to fix it. The window kept its size and glided across the string, which is why the sliding window is so fast.
💡 Interview Insight A common question is why we do not shrink maxFreq when lower moves. Explain that best can only ever increase, so a stale maxFreq never shrinks the answer. It may keep the window a touch larger than strictly valid, but that never produces a wrong best.
This is a big jump from the brute force. We went from O(n squared) down to O(n) by never recounting old letters. The window remembers what it holds and updates only at the edges.
Both approaches return the same answer. They just pay different prices to get there.
| Approach | Time | Space | Note |
|---|---|---|---|
| Brute force | O(n squared) | O(1) | Easy to trust, but recounts every window |
| Sliding window | O(n) | O(1) | Fast, visits each letter a fixed number of times |
In an interview, mention the brute force first to show you understand the problem. Then move to the sliding window as your real answer. Explain the replacements formula clearly, since that is the insight the interviewer is really testing.
A few small traps catch beginners on this problem. Keep them in mind.
Run an all-same string and an empty string through your code before you call it done. Those edge cases catch more bugs than any ordinary input.
A: k is the number of characters you are allowed to replace. Inside any window, you keep the most common letter and change the rest. As long as the changes needed stay within k, the window is valid.
A: The best answer can only grow, never shrink. A stale maxFreq may keep the window a little larger than strictly valid, but it never makes the final answer wrong. Skipping the recount keeps the code fast and simple.
A: It runs in O(n) time. Both the higher and lower pointers move forward at most n times across the whole string. Space is O(1) because the count array is always 26 slots.
A: The 26-slot array assumes uppercase A to Z, since it uses ch minus ‘A’ for the index. For lowercase or mixed input, switch to a HashMap that can count any character.
Longest Repeating Character Replacement in Java teaches a habit you will reuse across many problems: keep a window and count what is inside it. Once you see that changes needed equal window length minus the most common letter count, the whole thing falls into place.
Our seven-letter trace showed the payoff clearly. The brute force checked every window and found 4, but it recounted from scratch each time. The sliding window found the same 4 in one pass, sliding lower forward only when the window broke the rule.
So take the pattern, not just the answer. When a problem asks for the longest or shortest window under some rule, reach for two pointers and a running count. That sliding window habit will serve you well on the harder problems waiting further down the list.