Skip to content

Commit f72cb60

Browse files
Add 'Longest Substring with At Most Two Distinct Characters'
1 parent d9ca827 commit f72cb60

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ A collection of LeetCode solutions
4242

4343
[Longest Repeating Character Replacement](./src/longest_repeating_character_replacement.py)
4444

45+
[Longest Substring with At Most Two Distinct Characters](./src/longest_substring_with_at_most_two_distinct_characters.py)
46+
4547
[Longest Substring Without Repeating Characters](./src/test_longest_substring_without_repeating_characters.py)
4648

4749
[Lowest Common Ancestor of a Binary Search Tree](./src/lowest_common_ancestor_of_a_binary_search_tree.py)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
159. Longest Substring with At Most Two Distinct Characters
3+
4+
https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters
5+
6+
NOTES
7+
* Use a sliding window.
8+
9+
This one is pretty easy. As with other substring problems, we can use a hash
10+
map to track the frequency of characters in the current substring. Since,
11+
however, we are only concerned with at most two characters, we can just use two
12+
variables. Similar to 'Longest Substring Without Repeating Characters', we
13+
maintain the index of the last occurrence of each character in order to adjust
14+
the sliding window if the substring becomes invalid. In addition, I've added a
15+
solution that generalizes to 'k' distinct characters.
16+
"""
17+
18+
19+
class Solution:
20+
def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
21+
# c1 and c2 hold the index of the last occurrence of a character.
22+
left, right, c1, c2, maximum_size = 0, 0, 0, 0, 0
23+
24+
while right < len(s):
25+
# NOTE: s[c1] != s[c2] handles the case where `s` starts with
26+
# repeating characters
27+
if s[right] not in [s[c1], s[c2]] and s[c1] != s[c2]:
28+
# Move `left` to the character after the next index.
29+
if c1 < c2:
30+
left, c1, c2 = c1 + 1, c2, right
31+
else:
32+
left, c2, c1 = c2 + 1, c1, right
33+
# Update the index of `c1` and `c2`, the last occurrence of each
34+
# character.
35+
elif s[right] == s[c1]:
36+
c1 = right
37+
else:
38+
c2 = right
39+
40+
maximum_size = max(maximum_size, right - left + 1)
41+
right += 1
42+
43+
return maximum_size
44+
45+
46+
class GeneralSolution:
47+
def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
48+
left, right, maximum_size = 0, 0, 0
49+
50+
# Maintain a mapping of character -> index of last occurrence.
51+
d: dict[str, int] = {}
52+
53+
while right < len(s):
54+
d[s[right]] = right
55+
56+
# If the map ever contains more than two keys, then the substring
57+
# contains more than two distinct characters.
58+
if len(d) > 2:
59+
# Move `left` to the character after the next index.
60+
next_index = min(d.values())
61+
left = next_index + 1
62+
# Remove the character from the hash map.
63+
del d[s[next_index]]
64+
65+
maximum_size = max(maximum_size, right - left + 1)
66+
right += 1
67+
68+
return maximum_size
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
159. Longest Substring with At Most Two Distinct Characters
3+
4+
https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters
5+
"""
6+
7+
from unittest import TestCase
8+
9+
from src.longest_substring_with_at_most_two_distinct_characters import (
10+
GeneralSolution,
11+
Solution,
12+
)
13+
14+
15+
class TestSolution(TestCase):
16+
def test_1(self):
17+
exp = 3
18+
assert Solution().lengthOfLongestSubstringTwoDistinct("eceba") == exp
19+
20+
def test_2(self):
21+
exp = 5
22+
assert Solution().lengthOfLongestSubstringTwoDistinct("ccaabbb") == exp
23+
24+
def test_3(self):
25+
exp = 1
26+
assert Solution().lengthOfLongestSubstringTwoDistinct("a") == exp
27+
28+
29+
class TestGeneralSolution(TestCase):
30+
def test_1(self):
31+
exp = 3
32+
assert GeneralSolution().lengthOfLongestSubstringTwoDistinct("eceba") == exp
33+
34+
def test_2(self):
35+
exp = 5
36+
assert GeneralSolution().lengthOfLongestSubstringTwoDistinct("ccaabbb") == exp
37+
38+
def test_3(self):
39+
exp = 1
40+
assert GeneralSolution().lengthOfLongestSubstringTwoDistinct("a") == exp

0 commit comments

Comments
 (0)