This lesson on Arrays & Strings: Two Pointers and Sliding Window is hands-on and example-driven. You will master the inward two-pointer technique to verify symmetric sequence properties like palindromes in linear time and constant space. You will also learn to sanitize string data and combine nested pointer traversals with auxiliary hash sets to reduce combinatorial search spaces from O(N^3) to O(N^2) in Three Sum.
What You'll Be Able To Do
- Preprocess raw string inputs by lowercasing and stripping non-alphanumeric characters using Python string methods.
- Implement converging two-pointer loops to verify symmetric string properties in O(N) time and O(1) space.
- Formulate complement equations to transition from a brute-force O(N^3) search to an O(N^2) solution for Three Sum.
- Utilize auxiliary hash sets to achieve O(1) lookup times for target complements during array traversals.
- Identify deduplication constraints and avoid recording redundant combinations during multi-pointer sweeps.
Detailed Concept Walkthrough
1. String Preprocessing and Sanitization
Algorithmic constraints often require comparing logical alphanumeric tokens while ignoring casing, spaces, and punctuation. Sanitizing the data beforehand normalizes the input space for predictable index traversal.
- Mechanism: Raw input strings are filtered character-by-character to retain only alphanumeric characters (
c.isalnum()) while transforming remaining characters to lowercase (c.lower()). - Under the Hood: Creating a new sanitized string via list comprehension and
join()allocates O(N) extra space in memory and processes the string in a single linear pass. - Best Practice: When strict O(1) auxiliary space is required, skip invalid characters directly on the fly within the two-pointer loop rather than creating an intermediate preprocessed string.
raw_str = "A man, a plan, a canal: Panama"
# Filter non-alphanumeric characters and convert to lowercase
clean_chars = [c.lower() for c in raw_str if c.isalnum()]
sanitized_str = "".join(clean_chars)
# sanitized_str is now 'amanaplanacanalpanama'
Key Takeaway: Always normalize casing and discard non-essential characters before executing symmetry checks.
2. Inward Two-Pointer Convergence
Symmetry problems such as palindromes mirror characters across a central axis. Two pointers initialized at opposing ends move inward simultaneously to confirm or invalidate structural symmetry.
- Mechanism: Initialize pointer
P1at index 0 andP2at indexlen(s) - 1, iterating whileP1 < P2. If characters atP1andP2match, advanceP1forward and decrementP2backward. - Execution Flow: A single character mismatch immediately invalidates the palindrome condition and terminates the search early, returning false.
- Under the Hood: Inward traversal visits at most N/2 character pairs, ensuring optimal O(N) runtime without allocating additional string reversal buffers in memory.
def is_palindrome(s: str) -> bool:
p1, p2 = 0, len(s) - 1
while p1 < p2:
if s[p1] != s[p2]:
return False # Early exit on mismatch
p1 += 1
p2 -= 1
return True
Key Takeaway: Converging pointers terminate immediately on mismatch, evaluating symmetry in O(N) time with O(1) auxiliary space.
3. Structuring the Three Sum Search Space
Finding unique triplets summing to zero (i + j + k = 0) naive brute-force generates an intractable O(N^3) search space. Fixing one index turns the problem into finding two remaining elements that sum to the fixed value's negation.
- Mechanism: Iterate an outer pointer
ithrough the array from0toN - 1. For each fixed elementnums[i], the problem reduces to finding two subsequent elementsnums[j]andnums[k]such thatnums[j] + nums[k] = -nums[i]. - Execution Flow: A nested loop or secondary search explores the remaining subarray to the right of index
ito prevent reusing identical indices and redundant combinations. - Best Practice: Reduce combinatorial search complexity by anchoring one degree of freedom and executing optimized pair searches on the residual subspace.
nums = [-1, 0, 1, 2, -1, -4]
n = len(nums)
# Outer loop fixes the first element of the potential triplet
for i in range(n):
target_complement = -nums[i]
# Inner search explores combinations in nums[i+1:]
Key Takeaway: Fixing one element reduces an N-sum problem to an (N-1)-sum subproblem.
4. Complement Lookup via Auxiliary Hash Sets
Pair search inside the inner loop is accelerated from O(N) to O(1) average lookup time by maintaining a hash set of observed values.
- Mechanism: As the inner pointer
jiterates through elements followingi, calculate the exact required third value:complement = -nums[i] - nums[j]. Check ifcomplementis already stored in the hash setseen. - Under the Hood: A hash set leverages hash-based indexing to achieve O(1) expected time complexity for membership queries, reducing the overall Three Sum runtime to O(N^2).
- Best Practice: Populate the
seenset incrementally during inner traversal rather than pre-populating all elements, avoiding self-collisions and invalid index reuse.
def find_triplets_with_set(nums: list[int]) -> set[tuple[int, ...]]:
triplets = set()
for i in range(len(nums)):
seen = set()
for j in range(i + 1, len(nums)):
complement = -nums[i] - nums[j]
if complement in seen:
# Sort triplet tuple to assist with set-based deduplication
triplets.add(tuple(sorted([nums[i], nums[j], complement])))
seen.add(nums[j])
return triplets
Key Takeaway: Auxiliary hash sets replace linear inner-loop scanning with instant O(1) complement lookups.
Topics Covered in Arrays & Strings: Two Pointers and Sliding Window
- Two Pointers and Palindrome Definition (0:34 - 1:45) — The instructor defines the Two Pointer technique and visualizes string symmetry using a mirror analogy.
- Preprocessing Strings for Algorithmic Checks (1:45 - 3:31) — String sanitization is demonstrated by lowercasing characters and filtering non-alphanumeric inputs before comparing methods.
- Implementing Inward Two Pointer Search (3:31 - 5:53) — The instructor writes the converging two-pointer loop structure to compare string characters iteratively.
- Structuring the Three Sum Problem (5:53 - 7:50) — The lesson transitions from Two Sum to Three Sum by fixing one index and configuring nested traversal.
- Combining Pointers with Auxiliary Hash Sets (7:50 - 10:58) — An auxiliary hash set is introduced to locate complement values in constant time while addressing triplet deduplication.
DSA for Interviews Cheat Sheet
-
c.isalnum()— Checks if character is alphanumericis_valid = 'a'.isalnum() -
s.lower()— Converts string characters to lowercaseclean_s = 'AbC'.lower() -
while left < right:— Runs inward converging pointer loopwhile left < right: left += 1 right -= 1 -
seen = set()— Initializes empty hash set for O(1) lookupsseen = set() seen.add(nums[j]) -
target = -nums[i] - nums[j]— Calculates third complement for zero sumcomplement = -nums[i] - nums[j] -
triplets.add(tuple(sorted(...)))— Inserts sorted tuple to deduplicate tripletsres.add(tuple(sorted([a, b, c])))
Comparison Table
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Inward Two Pointers (Palindrome) | O(N) | O(1) |
| String Reversal Hack (s == s[::-1]) | O(N) | O(N) |
| Three Sum (Brute Force 3-Loop) | O(N^3) | O(1) |
| Three Sum (Fixed Pointer + Hash Set) | O(N^2) | O(N) |
Common Pitfalls
- Mistake: Using string reversal slicing s[::-1] for palindrome checking in memory-constrained environments. Avoid: Use inward two pointers to compare characters in-place with O(1) extra space.
- Mistake: Comparing characters directly without stripping punctuation and standardizing letter case. Avoid: Filter inputs using isalnum() and lower() before or during pointer comparisons.
- Mistake: Pre-populating the entire hash set with all array elements in Three Sum. Avoid: Populate the hash set dynamically within the inner loop to prevent reusing the same index.
- Mistake: Emitting duplicate triplets in Three Sum when identical input values appear multiple times. Avoid: Use sorted tuples in a set or skip identical consecutive elements.
FAQs
- Why use two pointers instead of Python's string reversal slice s == s[::-1]? Slicing creates a full copy of the string in memory taking O(N) extra space. Two pointers inspect characters in-place, achieving O(1) auxiliary space and terminating early on mismatch.
- Why is the palindrome loop condition while left < right instead of while left <= right? When left equals right, both pointers point to the exact same middle character in an odd-length string. Comparing a character to itself is redundant.
- How does using a hash set improve the Three Sum runtime over brute-force? A brute-force three-nested loop inspects all triplets in O(N^3) time. A hash set checks if the exact needed complement exists in O(1) average time, reducing overall complexity to O(N^2).
Sliding Window: Course Supplement
The existing video focuses on inward two pointers. This companion adds sliding-window patterns; it does not claim that the video demonstrates them.
Fixed-size window
For every contiguous window of length k, add the entering value and remove the value that just left. This avoids recomputing each window from scratch.
def max_window_sum(nums, k):
if not 1 <= k <= len(nums):
raise ValueError("k must be between 1 and len(nums)")
window = sum(nums[:k])
best = window
for right in range(k, len(nums)):
window += nums[right] - nums[right - k]
best = max(best, window)
return best
This runs in O(N) time after the initial sum and O(1) extra space. Define the empty-input and invalid-k contract before using the pattern.
Variable-size window with a monotone constraint
Expand the right edge, update the window state, then move the left edge while the constraint is violated. For longest substring with at most k distinct characters, maintain character counts and shrink until the number of distinct keys is at most k.
def longest_at_most_k_distinct(text, k):
if k <= 0:
return 0
counts = {}
left = best = 0
for right, char in enumerate(text):
counts[char] = counts.get(char, 0) + 1
while len(counts) > k:
old = text[left]
counts[old] -= 1
if counts[old] == 0:
del counts[old]
left += 1
best = max(best, right - left + 1)
return best
Each index enters and leaves at most once, so the total time is O(N), not O(N²). The condition must be monotone under removing items from the left. For sums with negative numbers, “shrink while sum is too large” is generally not valid; choose a prefix-sum or other method instead.
Interview checklist
- Is the target a contiguous range?
- Is the window size fixed, or can it grow and shrink?
- What exact state is maintained (sum, frequency map, distinct count)?
- What condition triggers shrinking, and does shrinking restore validity monotonically?
- State boundary behavior for empty input and impossible window sizes.