Frequency maps, prefix-sum + hash for subarray sum K, anagram keys, complexity narration, and production counting metaphors.
O(n) memory for O(n) time — the trade builders under-explain
Production engineers use maps constantly (requests per endpoint, sets of ids) but under-explain them in interviews: they code a dict without saying what key means and what invariant the map holds. Hash + prefix patterns convert nested scans into one pass: frequency maps, “have I seen target−x”, prefix sums for range and subarray sum K. Complexity speech: O(n) average time, O(n) space — and when adversarial hashing or sorted alternatives matter.
Three workhorse moves. Frequency map: count occurrences; compare multisets (anagrams). Value→index / value→first-seen: pair problems, first unique. Prefix sum + map of prefix→count/index: subarray sum equals K, contiguous array (0/1 treated as −1/+1), longest subarray with sum K. Production: time-bucketed metrics, cumulative counters, “how many requests since midnight.”
Prefix definition: pref[0]=0; pref[i] = a[0]+…+a[i−1]. Sum of a[l..r−1] = pref[r]−pref[l]. Subarray sum = K ⇔ pref[r] − pref[l] = K ⇔ pref[l] = pref[r]−K. As you compute pref[r], look up how many prior prefixes equal pref[r]−K. That is the whole O(n) idea.
Frequency maps: anagrams and groups
Group Anagrams: key = sorted string or 26-tuple count; map key → list of words. Find All Anagrams in a String: sliding window of |p| with freq match — hybrid of L3+L4. Valid Anagram: one count array, increment/decrement. Narrate key design: collision-free for lowercase letters via 26-int tuple; sorted key is simpler but O(k log k) per word.
python
1GROUP ANAGRAMS — key design is the interview content23from collections import defaultdict45def group_anagrams(strs: list[str]) -> list[list[str]]:6 groups: dict[tuple[int, ...], list[str]] = defaultdict(list)7 for w in strs:8 cnt = [0] * 269 for ch in w:10 cnt[ord(ch) - 97] += 111 groups[tuple(cnt)].append(w)12 return list(groups.values())1314# O(n * k) time for n words length k; space O(n k).15# Sorted-key alternative: key=''.join(sorted(w)) — simpler, slower per word.
Two Sum again — hash as first-class pattern
You saw Two Sum in L1 as rubric. Here it is pure hash pattern: one pass, need = target−x. Variants: all pairs (careful with duplicates), Two Sum BST, Two Sum less than K. Always state if modification of input is allowed (sort+two pointers alternative).
Subarray Sum Equals K
This is the flagship prefix+hash problem. Running sum s; ans += count[s−k]; count[s]++. Initialize count[0]=1 for subarrays from index 0. Negatives allowed — window fails, prefix works. Production metaphor: number of time ranges where error_count increased by K.
python
1SUBARRAY SUM EQUALS K — prefix + hashmap23from collections import defaultdict45def subarray_sum(nums: list[int], k: int) -> int:6 count: dict[int, int] = defaultdict(int)7 count[0] = 18 s = 09 ans = 010 for x in nums:11 s += x12 ans += count[s - k]13 count[s] += 114 return ans1516# Dry-run nums=[1,2,3], k=3:17# x=1 s=1 ans+=count[-2]=0 count={0:1,1:1}18# x=2 s=3 ans+=count[0]=1 -> [1,2]19# x=3 s=6 ans+=count[3]=1 -> [3]; total 2 ✓
Contiguous Array (equal 0s and 1s): map 0→−1, track first index of each prefix; when prefix repeats, subarray between has sum 0 ⇒ equal 0/1. Same family, different payload in the map (index vs count).
Interview complexity speech
Say out loud: average O(n) time for hash ops; worst-case pathological hashing is rare in interviews — they want the average case. Space O(n) for the map. If interviewer asks to reduce space, discuss sort+two pointers when applicable, or streaming approximations (outside classic coding). For “top-K endpoints last hour” → frequency map then heap (next lesson) or counter.most_common — name the structure pipeline.
Order of operations and reconstruction
Lookup-before-insert is non-negotiable for pair and prefix problems. For Two Sum, inserting before lookup can pair an element with itself when 2*x = target. For subarray sum K, inserting before lookup double-counts the empty contribution in some variants. Say the order out loud: “check need, then record current.”
When asked to return the subarray (not the count), store first index of each prefix or keep parent pointers — still O(n). When asked all subarrays, you may need lists of indices per prefix (watch memory). Clarify output shape before choosing map payload.
python
1LONGEST SUBARRAY WITH SUM K (positives only → window; general → prefix index map)23def longest_sum_k_general(nums: list[int], k: int) -> int:4 first: dict[int, int] = {0: -1} # prefix -> earliest index5 s = 06 best = 07 for i, x in enumerate(nums):8 s += x9 if s - k in first:10 best = max(best, i - first[s - k])11 if s not in first:12 first[s] = i # keep earliest for longest13 return best1415# Contrast: if all nums > 0, variable window with sum shrink is enough.16# Naming that branch is Problem Solving credit.
Anagram window hybrid
Find All Anagrams is sliding window of fixed width |p| with a freq-diff counter: when diff hits zero, record start. It is L3+L4 fused — fixed window + multiset equality. Interviewers use it to see if you can compose patterns, not just name them in isolation.
Hash/prefix rounds test whether you can invent the key meaning under time pressure. Drill: restate subarray-sum-K from the identity pref[r]−pref[l]=K with eyes closed.
01“Subarray sum equals K with negatives?” → prefix sum + hash count; not sliding window.
02“Why count[0]=1?” → empty prefix: whole prefix equals K from start.
03“Group anagrams key?” → 26-count tuple or sorted string; trade O(k) vs O(k log k).
04“Top-K endpoints last hour?” → freq map in window, then heap of size K (L5).
05“Two Sum space vs sort?” → hash O(n)/O(n); sort+pointers O(n log n)/O(1) loses indices unless stored.
06“Contiguous array equal 0/1?” → 0→−1, first-seen prefix index, max length on repeat prefix.
07“When is window better than prefix?” → non-negative + monotonic constraint on contiguous segment.
08“What do you say before coding?” → key meaning, update order (lookup then insert), complexity.
09"Why counts[0]=1?" -> empty prefix so subarrays starting at 0 match when prefix==K.
10"Anagram key sort vs freq?" -> sort simpler; vector O(k) for fixed alphabet.
11"Top K heap vs bucket?" -> heap O(n log k); bucket O(n) when freq range is n.
12"Prefix mod K?" -> same remainder => window sum divisible by K.
13"Hash vs two pointers for pair sum?" -> unsorted/indices => hash; sorted + O(1) space => two pointers.
Idioms. Complement lookup (Two Sum). Frequency counters (anagrams, majority, top-K). Prefix-sum + map (subarray sum K; init counts[0]=1). Prefix mod K for divisible-by-K. Group anagrams: sorted-tuple key vs char-frequency vector (vector wins for small alphabets). Top K: bucket O(n) vs heap O(n log k). LRU/rate-limiter teasers (full builds in L8): map+DLL; map user->bucket. Production: Python dict ~1.5-2x memory open-addressing; Java HashMap degrades on bad hashes; Go maps not concurrent-safe for mixed R/W; randomized hashing fights adversarial keys. Walk the narrative: partner existence is hash lookup; repeated range sums are prefix; how many windows sum to K is prefix plus frequency of prior prefixes. Dry-run [1,2,3] k=3 with the map evolving: after 1, {0:1,1:1}; after 2, prefix=3 hits 0; after 3, prefix=6 hits 3 — two windows. That dry-run is the Verification score. When n is huge and memory is tight, say you would spill to sort+two-pointers or external sort without abandoning hash for default constraints. Interview drill: on a blank pad, write the evolving prefix map for three arrays — [1,1,1] k=2, [1,-1,0] k=0, and [3,4,7,2,-3,1,4,2] k=7 — before you touch a keyboard. If you cannot predict the answer count from the map alone, you do not own the pattern yet. For group anagrams, say whether the alphabet is lowercase English (vector) or unicode (sort or Counter frozenset). For top-K, if k is close to n, sorting may beat a heap in practice — name the constant-factor tradeoff. LRU and rate-limiter designs are the bridge to L8: the same hash that gives O(1) lookup forces you to pick a second structure for order or time.
python
1SUBARRAY SUM EQUALS K — prefix + hashmap (LC 560)23from collections import defaultdict45def subarray_sum(nums: list[int], k: int) -> int:6 counts: dict[int, int] = defaultdict(int)7 counts[0] = 1 # empty prefix — critical8 prefix = ans = 09 for x in nums:10 prefix += x11 ans += counts[prefix - k]12 counts[prefix] += 113 return ans1415# [1,2,3] k=3 -> 2. Forgetting counts[0]=1 drops index-0 starts.16# Ledger metaphor: balance-K lookup counts windows that sum to K.17# Negatives allowed: same code. If only positives, sliding window is an alt family.
python
1GROUP ANAGRAMS — frequency-tuple key23from collections import defaultdict45def group_anagrams(strs: list[str]) -> list[list[str]]:6 buckets: dict[tuple, list[str]] = defaultdict(list)7 for s in strs:8 freq = [0] * 269 for ch in s:10 freq[ord(ch) - 97] += 111 buckets[tuple(freq)].append(s)12 return list(buckets.values())1314# Alt key=tuple(sorted(s)): simpler, O(k log k) per string. State tradeoff.15# Unicode input: Counter(s) items sorted into a tuple, or sorted(s) if comparable.
Graders + tiers. Name complement vs prefix+map; when hash loses to two pointers (sorted O(1) space); counts[0]=1; no list keys; edges empty/single/zeros/negatives. FAANG mixes two-sum with design (LRU). India OA: LC 1/49/560 speed. AI lab: sharded maps, bloom false positives, concurrent maps. Stripe-style risk systems and CDN caches are the production stories to name unprompted when they ask why hash. Complexity must include average vs worst hash under adversarial keys if the company is security-sensitive. Resources to keep open while drilling: NeetCode arrays/hashing playlist, TIH patterns, and the LeetCode discuss thread on subarray-sum-equals-k general guidance. Also drill LC 525 Contiguous Array and LC 974 Subarray Sums Divisible by K as the same ledger shape with a transformed value (0/1 as -1/+1, or prefix mod K). If you can reduce a new prompt to ledger + map in under two minutes, the pattern is installed.
Interview: “top-K most frequent API endpoints in the last hour.” Structure sketch?
AOnly a heap of endpoints, no countsBFrequency map (endpoint→count) over the hour window, then min-heap of size K by count (or counter top-K)CSort all endpoints alphabetically
Group Anagrams: interviewer asks why not use the string itself as map key.
ABecause strings are immutable in Python so cannot be keysBAnagrams are different strings — you need a canonical key (sorted / count tuple) so they collide into one bucketCYou must use hashing of hashcodes of chars only
You say “O(n) hash map” — interviewer asks worst case. Best senior reply?
AHash maps are always O(1); worst case does not existBAverage O(1) ops / O(n) total; pathological collisions are rare in interviews; if required we can discuss sort-based alternatives or tree maps O(log n)CSwitch immediately to O(n²) nested loops to avoid hashes