Skip to content

Commit e39dabd

Browse files
committed
feat(algorithms, sliding-window): longest repeating character replacement
1 parent 577602f commit e39dabd

28 files changed

Lines changed: 122 additions & 91 deletions

algorithms/sliding_window/longest_repeating_char_replacement/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ uppercase English character. You can perform this operation at most k times.
55

66
Return the length of the longest substring containing the same letter you can get after performing the above operations.
77

8+
## Examples
9+
810
Example 1:
911

1012
Input: s = "ABAB", k = 2
@@ -16,3 +18,60 @@ Input: s = "AABABBA", k = 1
1618
Output: 4
1719
Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA".
1820
The substring "BBBB" has the longest repeating letters, which is 4.
21+
22+
## Solution
23+
24+
In order for a substring to be valid, k + the frequency of the most frequent character in the substring must be greater
25+
than or equal to the length of the substring.
26+
27+
For example, if k = 2, and the substring is AABBB, then the most frequent character is B, which shows up 3 times. The
28+
substring is valid because 2 + 3 >= 5.
29+
30+
![Solution Sample 1](./images/solutions/longest_repeating_char_replacement_solution_sample_1.png)
31+
32+
However, if k = 2 and the substring is AAABBB, then the substring is invalid because 2 + 3 < 6.
33+
34+
![Solution Sample 2](./images/solutions/longest_repeating_char_replacement_solution_sample_2.png)
35+
36+
We use this fact to solve this problem with a variable-length sliding window to iterate over all valid substrings, and
37+
return the longest of those lengths at the end. To represent the state of the current window, we keep track of two
38+
variables:
39+
40+
- `state`: A dictionary mapping each character to the number of times it appears in the current window.
41+
- `max_freq`: The maximum number of times a single character has appeared in any window so far.
42+
43+
We start by extending the current window until it becomes invalid (i.e. `k` + `max_freq` < `window` length).
44+
45+
![Solution 1](./images/solutions/longest_repeating_char_replacement_solution_1.png)
46+
![Solution 2](./images/solutions/longest_repeating_char_replacement_solution_2.png)
47+
![Solution 3](./images/solutions/longest_repeating_char_replacement_solution_3.png)
48+
![Solution 4](./images/solutions/longest_repeating_char_replacement_solution_4.png)
49+
![Solution 5](./images/solutions/longest_repeating_char_replacement_solution_5.png)
50+
![Solution 6](./images/solutions/longest_repeating_char_replacement_solution_6.png)
51+
![Solution 7](./images/solutions/longest_repeating_char_replacement_solution_7.png)
52+
![Solution 8](./images/solutions/longest_repeating_char_replacement_solution_8.png)
53+
![Solution 9](./images/solutions/longest_repeating_char_replacement_solution_9.png)
54+
![Solution 10](./images/solutions/longest_repeating_char_replacement_solution_10.png)
55+
![Solution 11](./images/solutions/longest_repeating_char_replacement_solution_11.png)
56+
![Solution 12](./images/solutions/longest_repeating_char_replacement_solution_12.png)
57+
![Solution 13](./images/solutions/longest_repeating_char_replacement_solution_13.png)
58+
59+
At this point, the longest substring we have found so far is BCBAB, which has a length of 5 (max_freq = 3 + k = 2).
60+
Now, whenever we try to extend the current window to length 6, it will be invalid unless the character we just included
61+
in the window shows up 4 times. So each time we increase the window to length 6, we immediately shrink the window to
62+
length 5 until we find a character which shows up 4 times.
63+
64+
![Solution 14](./images/solutions/longest_repeating_char_replacement_solution_14.png)
65+
![Solution 15](./images/solutions/longest_repeating_char_replacement_solution_15.png)
66+
![Solution 16](./images/solutions/longest_repeating_char_replacement_solution_16.png)
67+
![Solution 17](./images/solutions/longest_repeating_char_replacement_solution_17.png)
68+
![Solution 18](./images/solutions/longest_repeating_char_replacement_solution_18.png)
69+
![Solution 19](./images/solutions/longest_repeating_char_replacement_solution_19.png)
70+
71+
This continues until we reach the end of the string, at which point we return the length of the longest substring we
72+
have found so far.
73+
74+
![Solution 20](./images/solutions/longest_repeating_char_replacement_solution_20.png)
75+
![Solution 21](./images/solutions/longest_repeating_char_replacement_solution_21.png)
76+
![Solution 22](./images/solutions/longest_repeating_char_replacement_solution_22.png)
77+
![Solution 23](./images/solutions/longest_repeating_char_replacement_solution_23.png)

algorithms/sliding_window/longest_repeating_char_replacement/__init__.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
from typing import DefaultDict, Dict
2+
from collections import defaultdict
3+
4+
15
def character_replacement(s: str, k: int) -> int:
26
"""
37
finds the length of the longest substring with repeating characters after performing at most k replacements.
@@ -42,25 +46,44 @@ def character_replacement(s: str, k: int) -> int:
4246
4347
Args:
4448
s(str): input string
45-
k(int): number of replacements
49+
k(int): number of replacements.
4650
Returns:
4751
int: length of the longest substring with repeating characters after at most k replacements
4852
"""
49-
frequency_map = {}
53+
if not s:
54+
return 0
55+
56+
frequency_map: DefaultDict[str, int] = defaultdict(int)
5057
result = 0
5158
left = 0
5259
max_frequency = 0
5360

54-
for right in range(len(s)):
55-
char = s[right]
56-
frequency_map[char] = 1 + frequency_map.get(char, 0)
61+
for idx, char in enumerate(s):
62+
frequency_map[char] += 1
5763
max_frequency = max(max_frequency, frequency_map[char])
5864

59-
while (right - left + 1) - max_frequency > k:
65+
while (idx - left + 1) - max_frequency > k:
6066
frequency_map[s[left]] -= 1
6167
left += 1
6268

63-
window_size = right - left + 1
69+
window_size = idx - left + 1
6470
result = max(result, window_size)
6571

6672
return result
73+
74+
75+
def character_replacement_2(s: str, k: int) -> int:
76+
state: Dict[str, int] = {}
77+
max_freq = 0
78+
max_length = 0
79+
start = 0
80+
81+
for end, char in enumerate(s):
82+
state[char] = state.get(char, 0) + 1
83+
max_freq = max(max_freq, state[char])
84+
85+
if k + max_freq < end - start + 1:
86+
state[s[start]] -= 1
87+
start += 1
88+
max_length = max(max_length, end - start + 1)
89+
return max_length
36.5 KB
Loading
47.8 KB
Loading
47.7 KB
Loading
46.4 KB
Loading
47.7 KB
Loading
46.4 KB
Loading
45.1 KB
Loading
45.8 KB
Loading

0 commit comments

Comments
 (0)