Skip to content

Commit 9fcfd74

Browse files
authored
Merge pull request #19 from dchou1618/feat/hash-table-3
test sliding window funcs
2 parents cb62e9d + c7bb4d3 commit 9fcfd74

2 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from collections import OrderedDict
2+
from pydantic import BaseModel, Field
3+
from typing import List
4+
5+
class SlidingWindowParam(BaseModel):
6+
s: str = Field(..., description="Input string", min_length=1)
7+
8+
9+
def lengthOfLongestSubstringTwoDistinct(s: SlidingWindowParam) -> int:
10+
seen = OrderedDict()
11+
start = 0
12+
longest = 0
13+
for end in range(len(s)):
14+
if s[end] in seen:
15+
seen.move_to_end(s[end])
16+
seen[s[end]] = end
17+
if len(seen) > 2:
18+
_, idx = seen.popitem(last=False)
19+
start = idx+1
20+
longest = max(longest, end-start+1)
21+
return longest
22+
23+
def maximumUniqueSubarray(nums: List[int]) -> int:
24+
last_seen = dict()
25+
start, curr_sum, max_sum = 0, 0, 0
26+
for end in range(len(nums)):
27+
if nums[end] in last_seen:
28+
last_idx = last_seen[nums[end]]
29+
if last_idx >= start:
30+
curr_sum -= sum(nums[start:(last_idx+1)])
31+
start = last_idx + 1
32+
curr_sum += nums[end]
33+
max_sum = max(max_sum, curr_sum)
34+
last_seen[nums[end]] = end
35+
return max_sum

tests/test_longest_substring.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import pytest
2+
from pydantic import ValidationError
3+
from Algorithms.slidingWindow.longestUnique import lengthOfLongestSubstringTwoDistinct, maximumUniqueSubarray, SlidingWindowParam
4+
5+
# -------------------------
6+
# Basic correctness tests
7+
# -------------------------
8+
9+
@pytest.mark.parametrize(
10+
"input_str, expected",
11+
[
12+
("eceba", 3),
13+
("ccaabbb", 5),
14+
("ababffzzeee", 5),
15+
("aa", 2),
16+
("a", 1),
17+
("ab", 2),
18+
("abc", 2),
19+
],
20+
)
21+
def test_basic_cases(input_str, expected):
22+
param = SlidingWindowParam(s=input_str)
23+
assert lengthOfLongestSubstringTwoDistinct(param.s) == expected
24+
25+
@pytest.mark.parametrize(
26+
"nums, expected",
27+
[
28+
([4, 2, 4, 5, 6], 17), # example 1
29+
([5, 2, 1, 2, 5, 2, 1, 2, 5], 8), # example 2
30+
([1], 1),
31+
([1, 2], 3),
32+
([1, 1], 1),
33+
([1, 2, 3], 6),
34+
([3, 3, 3], 3),
35+
],
36+
)
37+
def test_basic_cases(nums, expected):
38+
assert maximumUniqueSubarray(nums) == expected
39+
40+
# -------------------------
41+
# Regression / tricky cases
42+
# -------------------------
43+
44+
def test_alternating_characters():
45+
param = SlidingWindowParam(s="abababab")
46+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 8
47+
48+
49+
def test_three_distinct_repeated():
50+
param = SlidingWindowParam(s="abcabcabc")
51+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 2
52+
53+
54+
def test_eviction_order_matters():
55+
# Breaks implementations that don't move_to_end
56+
param = SlidingWindowParam(s="abaccc")
57+
# Longest valid substring: "accc"
58+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 4
59+
60+
61+
def test_multiple_evictions():
62+
param = SlidingWindowParam(s="aabbccddeeff")
63+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 4
64+
65+
def test_duplicate_at_start():
66+
# Forces window to jump start
67+
nums = [2, 1, 2, 3, 4]
68+
# Best: [1,2,3,4]
69+
assert maximumUniqueSubarray(nums) == 10
70+
71+
72+
def test_duplicate_at_end():
73+
nums = [1, 2, 3, 4, 1]
74+
# Best: [1,2,3,4]
75+
assert maximumUniqueSubarray(nums) == 10
76+
77+
78+
def test_multiple_duplicates():
79+
nums = [1, 2, 1, 3, 2, 3, 4]
80+
# Best: [1,3,2,3,4] is invalid
81+
# Best valid: [1,3,2] or [2,3,4]
82+
assert maximumUniqueSubarray(nums) == 9
83+
84+
85+
def test_eviction_jump_needed():
86+
# Breaks incorrect start updates
87+
nums = [1, 2, 3, 2, 5]
88+
# Best: [3,2,5]
89+
assert maximumUniqueSubarray(nums) == 10
90+
91+
# -------------------------
92+
# Edge cases
93+
# -------------------------
94+
95+
def test_all_same_character():
96+
param = SlidingWindowParam(s="aaaaaaa")
97+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 7
98+
99+
100+
def test_two_characters_only():
101+
param = SlidingWindowParam(s="abababababab")
102+
assert lengthOfLongestSubstringTwoDistinct(param.s) == 12
103+
104+
def test_all_unique():
105+
nums = [1, 2, 3, 4, 5, 6]
106+
assert maximumUniqueSubarray(nums) == sum(nums)
107+
108+
109+
def test_all_same():
110+
nums = [7, 7, 7, 7]
111+
assert maximumUniqueSubarray(nums) == 7
112+
113+
114+
def test_large_values():
115+
nums = [10_000, 9_999, 10_000]
116+
assert maximumUniqueSubarray(nums) == 19_999
117+
118+
def test_strictly_increasing_then_repeat():
119+
nums = list(range(1, 100)) + [50]
120+
assert maximumUniqueSubarray(nums) == sum(range(1, 100))
121+
122+
123+
def test_alternating_pattern():
124+
nums = [1, 2] * 50
125+
assert maximumUniqueSubarray(nums) == 3 # [1,2]
126+
127+
# -------------------------
128+
# Pydantic validation tests
129+
# -------------------------
130+
131+
def test_empty_string_rejected():
132+
with pytest.raises(ValidationError):
133+
SlidingWindowParam(s="")
134+
135+
136+
def test_non_string_input():
137+
with pytest.raises(ValidationError):
138+
SlidingWindowParam(s=123)
139+
140+
141+
# -------------------------
142+
# Invariant-style tests
143+
# -------------------------
144+
145+
@pytest.mark.parametrize("s", [
146+
"abc",
147+
"aabcbcdbca",
148+
"xyzzzyx",
149+
"pwwkew",
150+
])
151+
def test_result_never_exceeds_string_length(s):
152+
param = SlidingWindowParam(s=s)
153+
result = lengthOfLongestSubstringTwoDistinct(param.s)
154+
assert 0 < result <= len(s)
155+
156+
@pytest.mark.parametrize("nums", [
157+
[1, 2, 3],
158+
[1, 2, 1, 2, 3],
159+
[5, 5, 5, 1, 2],
160+
[100, 200, 300, 100, 400],
161+
])
162+
def test_result_never_exceeds_sum(nums):
163+
result = maximumUniqueSubarray(nums)
164+
assert 0 < result <= sum(nums)

0 commit comments

Comments
 (0)