feat(content): test cases batch 2

This commit is contained in:
2025-05-24 21:00:16 +01:00
parent 2e323d7a06
commit 09ec96a282
10 changed files with 962 additions and 474 deletions

View File

@@ -10,11 +10,11 @@ patterns:
- sliding-window
description: |
Given a string `s`, find the length of the longest substring without repeating characters.
Given a string `s`, find the length of the **longest substring** without repeating characters.
constraints: |
- 0 <= s.length <= 5 * 10^4
- s consists of English letters, digits, symbols and spaces
- `0 <= s.length <= 5 × 10^4`
- `s` consists of English letters, digits, symbols and spaces
examples:
- input: 's = "abcabcbb"'
@@ -25,90 +25,146 @@ examples:
explanation: "The answer is 'b', with length 1."
- input: 's = "pwwkew"'
output: "3"
explanation: "The answer is 'wke', with length 3."
explanation: "The answer is 'wke', with length 3. Note that 'pwke' is a subsequence, not a substring."
explanation:
approach: |
1. Use a sliding window with left and right pointers
2. Maintain a set of characters in the current window
3. Expand right pointer, adding characters to the set
4. When a duplicate is found, shrink from left until duplicate is removed
5. Track maximum window size throughout
intuition: |
We're looking for the longest contiguous substring where all characters are unique.
A sliding window naturally represents a substring.
Imagine a window sliding across the string. The window represents our current substring candidate. We want to expand this window as much as possible while keeping all characters inside it unique.
When we encounter a duplicate, the current window is invalid. Instead of restarting
from scratch, we shrink the window from the left until the duplicate is removed.
This way, we never revisit characters unnecessarily.
Think of it like this: you're scanning through a document with a highlighter. You want to find the longest stretch you can highlight where no letter appears twice. When you hit a repeat, you need to move the start of your highlight forward until the duplicate is gone.
The key insight is that we don't need to restart from scratch when we find a duplicate. If we've seen 'a' before at position 3, and we see 'a' again at position 7, we just need to move our window's left edge past position 3. Everything between positions 4 and 7 might still be valid!
This is the **sliding window** pattern: expand the right edge to explore, contract the left edge to maintain validity.
approach: |
We solve this using a **Sliding Window with a Set**:
**Step 1: Initialise the window and tracking**
- `left = 0`: Left edge of our window
- `char_set = set()`: Characters currently in our window
- `max_length = 0`: Best length found so far
- The right edge is controlled by our loop iteration
&nbsp;
**Step 2: Expand the window (right pointer)**
- For each character at position `right`:
- If `s[right]` is already in `char_set`, we have a duplicate
- Before adding it, we must shrink from the left until the duplicate is removed
&nbsp;
**Step 3: Shrink the window (left pointer)**
- While `s[right]` is in `char_set`:
- Remove `s[left]` from the set
- Increment `left`
- This "slides" the window past the previous occurrence
&nbsp;
**Step 4: Add current character and update maximum**
- Add `s[right]` to `char_set`
- Update `max_length = max(max_length, right - left + 1)`
&nbsp;
The window always contains unique characters, and we track the maximum size it achieves.
common_pitfalls:
- title: Resetting the window incorrectly
- title: Resetting the Window Completely on Duplicate
description: |
When finding a duplicate, don't start over from the next character.
Instead, shrink from the left until the duplicate is removed.
A common mistake is to set `left = right` when finding a duplicate, effectively restarting the search. This loses valid characters that could still be part of a longer substring.
For example, in `"abcdb"`, when we hit the second `'b'`, we should move `left` from 0 to 2 (just past the first `'b'`), keeping `"cd"` in our window. Resetting to `left = right` would discard `"cd"` unnecessarily.
wrong_approach: "left = right when duplicate found"
correct_approach: "Increment left until duplicate is out of window"
correct_approach: "Increment left until duplicate is removed from window"
- title: Not handling empty string
- title: Off-by-One in Length Calculation
description: |
An empty string should return 0. Make sure the algorithm handles this.
The length of a window from index `left` to `right` (inclusive) is `right - left + 1`, not `right - left`.
- title: Off-by-one in length calculation
For `left = 2, right = 5`, the substring has 4 characters (indices 2, 3, 4, 5), not 3.
wrong_approach: "max_length = max(max_length, right - left)"
correct_approach: "max_length = max(max_length, right - left + 1)"
- title: Not Handling Empty String
description: |
Window length is right - left + 1, or just track max before removing.
An empty string `""` should return `0`. The algorithm handles this naturally (the loop never executes), but it's worth verifying.
Similarly, a single character `"a"` should return `1`.
wrong_approach: "Assuming string has at least one character"
correct_approach: "Algorithm works for empty strings — returns 0"
key_takeaways:
- Sliding window is ideal for substring problems with constraints
- Use a set or map to track elements in current window
- Shrinking from one end maintains the contiguous property
- This pattern appears in many "longest/shortest with constraint" problems
- "**Sliding window for substrings**: When looking for contiguous sequences with constraints, sliding window is often the answer"
- "**Expand and contract**: Right pointer explores, left pointer maintains validity"
- "**Set for uniqueness checking**: O(1) membership testing makes the algorithm efficient"
- "**Optimisation with hash map**: Store the last index of each character to jump `left` directly instead of incrementing"
time_complexity: "O(n)"
space_complexity: "O(min(m, n))"
complexity_explanation: |
Time: Each character is visited at most twice (once by right, once by left).
Space: The set holds at most min(n, m) characters where m is the charset size.
time_complexity: "O(n). Each character is visited at most twice — once by the right pointer, once by the left pointer."
space_complexity: "O(min(m, n)). The set holds at most min(n, m) characters, where m is the character set size (e.g., 26 for lowercase letters, 128 for ASCII)."
solutions:
- approach_name: Sliding Window with Set (Optimal)
- approach_name: Sliding Window with Set
is_optimal: true
code: |
def length_of_longest_substring(s: str) -> int:
# Set to track characters in current window
char_set = set()
left = 0
max_length = 0
# Right pointer expands the window
for right in range(len(s)):
# Shrink window until duplicate is removed
while s[right] in char_set:
char_set.remove(s[left])
left += 1
# Add current character to window
char_set.add(s[right])
# Update maximum length
max_length = max(max_length, right - left + 1)
return max_length
explanation: |
Expand window by moving right pointer.
When duplicate found, shrink from left until window is valid again.
**Time Complexity:** O(n) — Each character added and removed from set at most once.
- approach_name: Optimized with Hash Map
**Space Complexity:** O(min(m, n)) — Set holds unique characters in window.
We maintain a sliding window containing only unique characters. When we encounter a duplicate, we shrink from the left until it's removed. The window size at each step represents a valid substring length.
- approach_name: Optimised with Hash Map
is_optimal: true
code: |
def length_of_longest_substring(s: str) -> int:
# Map character to its most recent index
char_index = {}
left = 0
max_length = 0
for right, char in enumerate(s):
# If char seen before AND within current window
if char in char_index and char_index[char] >= left:
# Jump left pointer past the previous occurrence
left = char_index[char] + 1
# Update character's latest index
char_index[char] = right
# Update maximum length
max_length = max(max_length, right - left + 1)
return max_length
explanation: |
Store the last index of each character.
Jump left pointer directly past the duplicate instead of shrinking one by one.
**Time Complexity:** O(n) — Single pass through the string.
**Space Complexity:** O(min(m, n)) — Hash map stores character indices.
Instead of shrinking the window one character at a time, we store each character's last index. When we find a duplicate, we jump `left` directly past the previous occurrence. The condition `char_index[char] >= left` ensures we only consider duplicates within the current window (old occurrences outside the window are ignored).