-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Longest Repeating Character Replacement
- Loading branch information
Showing
6 changed files
with
173 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# [Longest Repeating Character Replacement](https://leetcode.com/problems/longest-repeating-character-replacement/description/) | ||
|
||
## Intuition | ||
|
||
The problem asks us to find the longest substring with the same character, allowing for at most `k` character | ||
replacements. The key insight is to use a sliding window approach, where we maintain a window that can be made valid ( | ||
all characters the same) by replacing at most `k` characters. We expand the window as long as we can make all characters | ||
in the window the same with at most `k` replacements. When we can't, we start shrinking the window from the left. | ||
|
||
## Approach | ||
|
||
The solution uses a sliding window technique with two pointers (`i` and `j`) and a `HashMap` to keep track of character | ||
frequencies. Here's how it works: | ||
|
||
1. Initialize variables: | ||
- `maxLength`: to store the length of the longest valid substring | ||
- `maxCount`: to keep track of the count of the most frequent character in the current window | ||
- `charCount`: a HashMap to store the frequency of each character in the current window | ||
2. Start with a window of size 1 (`i` at the start, `j` at the second character). | ||
3. Expand the window by moving `j` to the right: Add the new character to `charCount` and update `maxCount` if | ||
necessary. | ||
4. Check if the current window is valid: | ||
- A window is valid if `(window size - count of most frequent char) <= k` | ||
- If not valid, shrink the window from the left by moving `i` to the right and updating `charCount` and `maxCount`. | ||
5. If the window is valid, update `maxLength` if the current window is longer. | ||
6. Repeat steps 3-5 until `j` reaches the end of the string. | ||
|
||
## Complexity | ||
|
||
- **Time Complexity: `O(n)`** as we iterate through the string once with the `j` pointer. The `i` pointer also moves | ||
forward, never backwards, so it also iterates through the string at most once. Operations on the hash map are | ||
constant. | ||
- **Space Complexity: `O(1)`** as the hash map stores at 26 entries (uppercase english letters). All other variables use | ||
constant space. | ||
|
||
## Code | ||
|
||
- [Java](../src/main/java/io/dksifoua/leetcode/longestrepeatingcharacterreplacement/Solution.java) | ||
|
||
## Summary | ||
|
||
This solution efficiently solves the problem by using a sliding window approach. It maintains a window that can be made | ||
valid with at most `k` replacements and expands or contracts this window as it moves through the string. The use of a | ||
hash map to track character frequencies allows for quick updates and checks. | ||
|
||
The algorithm is optimal in terms of time complexity, as it processes each character at most twice (once when adding to | ||
the window, once when removing). The space complexity is constant, making it memory-efficient for large inputs. | ||
|
||
One potential optimization could be to use an array of size 26 instead of a hash map, which might be slightly faster for | ||
very large inputs, but the current implementation is clean and easy to understand. |
56 changes: 56 additions & 0 deletions
56
src/main/java/io/dksifoua/leetcode/longestrepeatingcharacterreplacement/Solution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package io.dksifoua.leetcode.longestrepeatingcharacterreplacement; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class Solution { | ||
|
||
public int characterReplacement(String s, int k) { | ||
int maxLength = 1, maxCount = 1; | ||
Map<Character, Integer> charCount = new HashMap<>() {{ | ||
put(s.charAt(0), 1); | ||
}}; | ||
int i = 0, j = 1; | ||
while (i < s.length() && j < s.length()) { | ||
charCount.put(s.charAt(j), charCount.getOrDefault(s.charAt(j), 0) + 1); | ||
maxCount = Math.max(maxCount, charCount.get(s.charAt(j))); | ||
if (maxCount + k < j - i + 1) { | ||
if (charCount.get(s.charAt(i)) == maxCount) { | ||
maxCount -= 1; | ||
} | ||
charCount.put(s.charAt(i), charCount.get(s.charAt(i)) - 1); | ||
i += 1; | ||
} else { | ||
maxLength = Math.max(maxLength, j - i + 1); | ||
} | ||
|
||
j += 1; | ||
} | ||
|
||
return maxLength; | ||
} | ||
|
||
public int characterReplacementOptimal(String s, int k) { | ||
int maxLength = 1, maxCount = 1; | ||
int[] charCount = new int[26]; | ||
charCount[s.charAt(0) - 'A'] += 1; | ||
int i = 0, j = 1; | ||
while (i < s.length() && j < s.length()) { | ||
charCount[s.charAt(j) - 'A'] += 1; | ||
maxCount = Math.max(maxCount, charCount[s.charAt(j) - 'A']); | ||
if (maxCount + k < j - i + 1) { | ||
if (charCount[s.charAt(i) - 'A'] == maxCount) { | ||
maxCount -= 1; | ||
} | ||
charCount[s.charAt(i) - 'A'] -= 1; | ||
i += 1; | ||
} else { | ||
maxLength = Math.max(maxLength, j - i + 1); | ||
} | ||
|
||
j += 1; | ||
} | ||
|
||
return maxLength; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/test/java/io/dksifoua/leetcode/longestrepeatingcharacterreplacement/SolutionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.dksifoua.leetcode.longestrepeatingcharacterreplacement; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
public class SolutionTest { | ||
|
||
private final Solution solution = new Solution(); | ||
|
||
@Test | ||
void test1() { | ||
assertEquals(4, solution.characterReplacement("ABAB", 2)); | ||
} | ||
|
||
@Test | ||
void testOptimal1() { | ||
assertEquals(4, solution.characterReplacementOptimal("ABAB", 2)); | ||
} | ||
|
||
@Test | ||
void test2() { | ||
assertEquals(4, solution.characterReplacement("AABABBA", 1)); | ||
} | ||
|
||
@Test | ||
void testOptimal2() { | ||
assertEquals(4, solution.characterReplacementOptimal("AABABBA", 1)); | ||
} | ||
|
||
@Test | ||
void test3() { | ||
assertEquals(1, solution.characterReplacement("ABAB", 0)); | ||
} | ||
|
||
@Test | ||
void testOptimal3() { | ||
assertEquals(1, solution.characterReplacementOptimal("ABAB", 0)); | ||
} | ||
|
||
@Test | ||
void test4() { | ||
assertEquals(4, solution.characterReplacement("AAAA", 0)); | ||
} | ||
|
||
@Test | ||
void testOptimal4() { | ||
assertEquals(4, solution.characterReplacementOptimal("AAAA", 0)); | ||
} | ||
} |