Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Two Pointers & Sliding Window
MediumLC 438

Find All Anagrams in a String (LeetCode 438)

Target Frequency:GoogleAmazon

You have two strings, s and p. Find every starting index in s where the substring of length len(p) beginning there is an anagram of p — i.e. uses exactly the same letters, rearranged. Return those indices, in any order.

Worked Examples

Example 1
Input:s = "cbaebabacd", p = "abc"
Output:[0, 6]
Explanation: Starting at index 0, s gives "cba" — a rearrangement of "abc". Starting at index 6, s gives "bac" — also a rearrangement of "abc". Both indices qualify.
Example 2
Input:s = "abab", p = "ab"
Output:[0, 1, 2]
Explanation: Every length-2 window starting at index 0, 1, or 2 ("ab", "ba", "ab") uses the exact same two letters as p, so all three indices qualify.

⚖️Formal Constraints & Bounds

  • 1 <= s.length, p.length <= 3 * 104

  • s and p consist of lowercase English letters.

Deep-Dive & Conceptual Insights

Core Insight

Fixed-size windows transform quadratic re-evaluations into constant-time delta shifts.

Real-World Scenario & Production Applications

DNA motif discovery, substring frequency matching, and token sliding window verifications.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Target Frequency Baseline & Initial Window

Count character frequencies for pattern string p. Seed the rolling window with the first len(p) characters of s.

Mathematical Recurrence / Code Invariant
p_count = Counter(p)
s_count = Counter(s[:len(p)])
res = [0] if s_count == p_count else []

Step-by-Step Execution Trace Table

StepIndex iIncoming s[i]Outgoing s[i-len(p)]Active Windows_count == p_count?Result Indices
Init0..2"cba" (seed)—s[0..2] = "cba"True (Counts match)[0]
13'e''c's[1..3] = "bae"False ('e' not in p)[0]
24'b''b's[2..4] = "aeb"False ('e' not in p)[0]
35'a''a's[3..5] = "eba"False ('e' not in p)[0]
46'b''e's[4..6] = "bab"False ('b':2, 'a':1)[0]
57'a''b's[5..7] = "aba"False ('a':2, 'b':1)[0]
68'c''a's[6..8] = "bac"True (Counts match)[0, 6]
79'd''b's[7..9] = "acd"False ('d' not in p)[0, 6]
Code Shape
Archetype Code Shape
Python
1k = len(p)
2if len(s) < k:
3 return []
4p_count, window_count = [0] * 26, [0] * 26
5for ch in p:
6 p_count[ord(ch) - ord('a')] += 1
7for ch in s[:k]:
8 window_count[ord(ch) - ord('a')] += 1
9result = [0] if window_count == p_count else []
10for i in range(k, len(s)):
11 window_count[ord(s[i]) - ord('a')] += 1
12 window_count[ord(s[i - k]) - ord('a')] -= 1
13 if window_count == p_count:
14 result.append(i - k + 1)
15return result

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Fixed-size sliding windows are the rigid-boundary counterpart to dynamic windows. While dynamic windows (LC 209, LC 3, LC 76) grow and shrink adaptively based on constraint satisfaction, fixed-size windows enforce an immutable aperture width k=∣p∣k = |p|k=∣p∣ across the entire sequence.

🧭 Conceptual Foundation & Pattern Intuition

An anagram is any permutation of characters. The foundational insight is that two strings are anagrams if and only if their character frequency distributions are identical:

f⃗s=f⃗p  ⟺  substring s[L..R] is an anagram of p\vec{f}_s = \vec{f}_p \iff \text{substring } s[L..R] \text{ is an anagram of } pf​s​=f​p​⟺substring s[L..R] is an anagram of p

Because character arrangement inside the window is completely irrelevant, we never need to sort substrings or generate permutations. The algorithm only needs to verify whether the current kkk-length window matches ppp's character histogram.

A naive approach tests every candidate starting index i∈[0,∣s∣−∣p∣]i \in [0, |s| - |p|]i∈[0,∣s∣−∣p∣], taking a slice s[i..i+k−1]s[i..i+k-1]s[i..i+k−1] and counting or sorting its letters. Recomputing frequencies for kkk characters takes O(k)O(k)O(k) work per position. Across all (∣s∣−∣p∣+1)(|s| - |p| + 1)(∣s∣−∣p∣+1) windows, the total runtime degrades quadratically to:

Tnaive=O(∣s∣⋅∣p∣)T_{\text{naive}} = O(|s| \cdot |p|)Tnaive​=O(∣s∣⋅∣p∣)

When ∣s∣=30,000|s| = 30,000∣s∣=30,000 and ∣p∣=10,000|p| = 10,000∣p∣=10,000, this requires up to 3×1083 \times 10^83×108 operations—triggering a guaranteed Time Limit Exceeded (TLE) failure in technical interviews.

📐 Fixed-Width Viewport Mental Model

A fixed window of length k=∣p∣k = |p|k=∣p∣ maintains the strict invariant R−L+1=kR - L + 1 = kR−L+1=k. As the window shifts right by 1, only two characters change state: the exiting character on the left boundary is decremented, and the entering character on the right boundary is incremented.

Code / Blueprint
String s: | c | b | a | e | b | a | b | a | c | d |
Indices: 0 1 2 3 4 5 6 7 8 9
Target p: "abc" (len = 3) -> requires {a: 1, b: 1, c: 1}
 
Window 0: [ c b a ] -> freq: {a:1, b:1, c:1} == p -> MATCH! (Index 0)
slide +1: drop s[0]='c', add s[3]='e'
Window 1: [ b a e ] -> freq: {a:1, b:1, e:1} != p -> Miss
slide +1: drop s[1]='b', add s[4]='b'
Window 2: [ a e b ] -> freq: {a:1, b:1, e:1} != p -> Miss
...
Window 6: [ b a c ] -> freq: {a:1, b:1, c:1} == p -> MATCH! (Index 6)
 
🎞️ The Analogy: The Rigid Cardboard Stencil on a Conveyor Belt

Imagine an industrial conveyor belt carrying a continuous stream of letter tiles. Mounted directly above the belt is a stationary cardboard stencil with an open aperture window exactly kkk tiles wide.

When the conveyor belt rolls forward by one position:

  1. The k−1k - 1k−1 center tiles stay completely visible inside the aperture. Their counts do not change at all.
  2. Exactly one tile passes out of view on the left edge (outgoing = s[i - k]). We subtract it from our tally: window_count[outgoing] -= 1.
  3. Exactly one new tile rolls into view on the right edge (incoming = s[i]). We add it to our tally: window_count[incoming] += 1.

Re-counting all kkk tiles under the stencil on every step is redundant recomputation. By updating only the boundary delta (+1 incoming, -1 outgoing), state maintenance requires strictly O(1)O(1)O(1) constant operations per slide!

🔑 The Key Idea in the Code

There are two primary paradigms to manage and verify frequency equality:

Paradigm A: The Fixed 26-Element Array (O(Σ)O(\Sigma)O(Σ) Comparison)

Because strings consist of lowercase English letters, we allocate two 26-element arrays ([0] * 26):

Code / Blueprint
p_count, window_count = [0] * 26, [0] * 26
for ch in p:
p_count[ord(ch) - ord('a')] += 1
for ch in s[:k]:
window_count[ord(ch) - ord('a')] += 1 # First window: s[0..k-1]
 
result = [0] if window_count == p_count else []
for i in range(k, len(s)):
window_count[ord(s[i]) - ord('a')] += 1 # Add incoming s[i]
window_count[ord(s[i - k]) - ord('a')] -= 1 # Remove outgoing s[i - k]
if window_count == p_count: # O(26) = O(1) array equality check
result.append(i - k + 1) # Window s[i - k + 1..i]
 

Because the alphabet size Σ=26\Sigma = 26Σ=26 is constant, comparing the two 26-element arrays takes exactly 26 machine word comparisons—which is mathematically O(1)O(1)O(1). Total time is strictly O(∣s∣⋅26)=O(∣s∣)O(|s| \cdot 26) = O(|s|)O(∣s∣⋅26)=O(∣s∣).

Paradigm B: The FAANG O(1)O(1)O(1) Scalar matches Counter (Zero-Scan Optimization)

Can we evaluate whether the window is valid without even doing 26 comparisons per step? Yes. We maintain an integer register matches (from 000 to 262626), indicating how many of the 26 characters currently have matching counts in both sss and ppp.

  • On each slide, only two characters alter their frequencies: the incoming character and the outgoing character.
  • Before modifying a character's count, we check if it currently matches p_count. If modifying it breaks a match, we decrement matches -= 1.
  • After modifying its count, if it now equals p_count, we increment matches += 1.
  • The entire window is an anagram if and only if matches == 26! This evaluates window validity in strictly O(1)O(1)O(1) operations with zero array scanning, making it optimal even for large Unicode or ASCII alphabets.
⚖️ Brute Force vs. Fixed Sliding Window
DimensionBrute Force (Recompute Each Window)Fixed Sliding Window (Rolling Delta)
Time Complexity$O(s
Work per StepRe-scans entire kkk-character window (O(k)O(k)O(k) ops)Ingests 1 char, evicts 1 char (O(1)O(1)O(1) ops)
Auxiliary SpaceO(k)O(k)O(k) substring copiesO(1)O(1)O(1) fixed 26-element counters
Boundary InvariantUnmanagedRigid Aperture: R−L+1=kR - L + 1 = kR−L+1=k
💡 The 4-Step Fixed-Window Recipe for Interviews

Whenever an interview problem asks about a contiguous substring/subarray of fixed length kkk:

  1. Guard Condition: Immediately return empty/default if ∣s∣<k|s| < k∣s∣<k.
  2. Seed State: Process the first kkk elements into the window state accumulator (s[0..k-1]).
  3. Roll the Aperture: Iterate iii from kkk to ∣s∣−1|s|-1∣s∣−1. At each step:
    • Ingest s[i] into state.
    • Evict s[i - k] from state.
  4. Evaluate & Collect: Check state condition in O(1)O(1)O(1) and record the 0-indexed start position i - k + 1.
  • Recomputing frequency counts from scratch: Re-hashing the entire window in O(k) time degrades overall complexity to O(N * k). Maintain running counts with O(1) add and remove updates.

  • String length smaller than target: Failing to guard if len(s) < len(p): return [] causes out-of-bounds slice errors on initialization.

  • Comparing dictionary equality directly: In Python, comparing dict1 == dict2 takes O(26) = O(1) time, but in other languages maintain a scalar matches counter to avoid 26-key comparisons per step.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Contiguous subarray/substring of fixed length K property verification.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: window contains exactly K elements at all times after the initial seed.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • When s[i] enters, s[i - k] leaves: removing s[i - k + 1] instead shifts every window, so the windows compared are the wrong ones.

  • Compare window_count == p_count only after both the add and the remove: in between, the window holds k + 1 letters and never matches.

  • if len(s) < k: return [] before building the first window: no window fits.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use a Fixed Window of size k, the length of p. I count p's letters into p_count and the first k letters of s into window_count. Then I slide one step at a time: s[i] enters and s[i - k] leaves, so I add one count and subtract one, and after both updates I compare window_count with p_count; if they're equal, the window starting at i - k + 1 is an anagram. The trap is the leaving index: it's s[i - k], and an off-by-one there shifts every window. Comparing two 26-entry lists is constant work, so it's O(N) time and O(1) extra space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

O(|s|): the slide loop runs once per letter of s after the first window, and each step does one add, one remove and one 26-entry comparison.

SPACE COMPLEXITY

O(1) Auxiliary

Look at memory allocations: The algorithm executes iteratively with O(1) stack frames. Auxiliary heap memory is strictly bounded by two fixed 26-element integer frequency arrays (or hash maps bounded by alphabet size Σ = 26). Since the alphabet size is constant regardless of input length, auxiliary space is strictly O(1) Auxiliary.

Formal Recurrence Relation

T(N)=∣p∣+(∣s∣−∣p∣)⋅Σ=O(∣s∣)T(N) = |p| + (|s| - |p|) \cdot \Sigma = O(|s|)T(N)=∣p∣+(∣s∣−∣p∣)⋅Σ=O(∣s∣)

Derivation Progression

Initial Seed Accumulation

O(|p|)

Constructs frequency counts for pattern p and the first |p| characters of s in O(|p|) time.

Rolling Delta Update

window_count[in] += 1; window_count[out] -= 1

Each slide adds the new incoming right character and subtracts the old left character in O(1) scalar operations.

Frequency Vector Comparison

O(Σ)=O(26)=O(1)O(\Sigma) = O(26) = O(1)O(Σ)=O(26)=O(1)

Checks whether window character counts match pattern counts in constant time.

Total Linear Pass

∣s∣−∣p∣ sliding steps|s| - |p| \text{ sliding steps}∣s∣−∣p∣ sliding steps

Overall execution time is strictly linear in string length: O(|s|).

Variable Definitions

∣s∣|s|∣s∣

Length of the target string s (up to 30,000)

∣p∣|p|∣p∣

Length of the pattern/anagram string p

ΣΣΣ

Alphabet cardinality (26 lowercase English letters)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative

🔵 Auxiliary Heap

O(1) Two 26-element frequency arrays

🟢 Output Space

O(|s|) Result start indices array

Boundary Best / Worst Cases

Best Case

O(∣s∣)O(|s|)O(∣s∣)

Average Case

O(∣s∣)O(|s|)O(∣s∣)

Worst Case

O(∣s∣)O(|s|)O(∣s∣)

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "find all start indices of p's anagrams in s", "both strings consist of lowercase English letters". Fixed-size sliding window (K=∣p∣K = |p|K=∣p∣) with rolling frequency state.

CONSTRAINTS & BOUNDS

1≤∣s∣,∣p∣≤3×1041 \le |s|, |p| \le 3 \times 10^41≤∣s∣,∣p∣≤3×104, lowercase English letters only. Recounting every window is O(∣s∣⋅∣p∣)O(|s| \cdot |p|)O(∣s∣⋅∣p∣), up to about 2.3×1082.3 \times 10^82.3×108 steps (when p is half as long as s); sliding the counts costs one add, one remove and one 26-entry comparison per step, about 8×1058 \times 10^58×105.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale: with a large alphabet (Unicode text instead of 26 letters) the full comparison costs O(∣Σ∣)O(|\Sigma|)O(∣Σ∣) per step. There, keep a matches counter of letters whose counts agree, change it only when the entering or leaving letter's count reaches or leaves its target, and test matches == len(p_count). On a stream, the window must also keep its last k characters (a ring buffer), or it cannot know which one leaves.

Core Algorithmic State Invariants

  1. Counts Are All an Anagram Needs

An anagram is the same letters in any order, so the order inside the window never matters: two strings are anagrams exactly when their 26 letter counts are equal. That is why window_count == p_count is a complete test, with no sorting and no pairing of letters.

Theory Context•Two Pointers & Sliding Window
MediumLC 438

Find All Anagrams in a String (LeetCode 438)

Target Frequency:GoogleAmazon

You have two strings, s and p. Find every starting index in s where the substring of length len(p) beginning there is an anagram of p — i.e. uses exactly the same letters, rearranged. Return those indices, in any order.

Worked Examples

Example 1
Input:s = "cbaebabacd", p = "abc"
Output:[0, 6]
Explanation: Starting at index 0, s gives "cba" — a rearrangement of "abc". Starting at index 6, s gives "bac" — also a rearrangement of "abc". Both indices qualify.
Example 2
Input:s = "abab", p = "ab"
Output:[0, 1, 2]
Explanation: Every length-2 window starting at index 0, 1, or 2 ("ab", "ba", "ab") uses the exact same two letters as p, so all three indices qualify.

⚖️Formal Constraints & Bounds

  • 1 <= s.length, p.length <= 3 * 104

  • s and p consist of lowercase English letters.

Deep-Dive & Conceptual Insights

Core Insight

Fixed-size windows transform quadratic re-evaluations into constant-time delta shifts.

Real-World Scenario & Production Applications

DNA motif discovery, substring frequency matching, and token sliding window verifications.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Target Frequency Baseline & Initial Window

Count character frequencies for pattern string p. Seed the rolling window with the first len(p) characters of s.

Mathematical Recurrence / Code Invariant
p_count = Counter(p)
s_count = Counter(s[:len(p)])
res = [0] if s_count == p_count else []

Step-by-Step Execution Trace Table

StepIndex iIncoming s[i]Outgoing s[i-len(p)]Active Windows_count == p_count?Result Indices
Init0..2"cba" (seed)—s[0..2] = "cba"True (Counts match)[0]
13'e''c's[1..3] = "bae"False ('e' not in p)[0]
24'b''b's[2..4] = "aeb"False ('e' not in p)[0]
35'a''a's[3..5] = "eba"False ('e' not in p)[0]
46'b''e's[4..6] = "bab"False ('b':2, 'a':1)[0]
57'a''b's[5..7] = "aba"False ('a':2, 'b':1)[0]
68'c''a's[6..8] = "bac"True (Counts match)[0, 6]
79'd''b's[7..9] = "acd"False ('d' not in p)[0, 6]
Code Shape
Archetype Code Shape
Python
1k = len(p)
2if len(s) < k:
3 return []
4p_count, window_count = [0] * 26, [0] * 26
5for ch in p:
6 p_count[ord(ch) - ord('a')] += 1
7for ch in s[:k]:
8 window_count[ord(ch) - ord('a')] += 1
9result = [0] if window_count == p_count else []
10for i in range(k, len(s)):
11 window_count[ord(s[i]) - ord('a')] += 1
12 window_count[ord(s[i - k]) - ord('a')] -= 1
13 if window_count == p_count:
14 result.append(i - k + 1)
15return result

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Fixed-size sliding windows are the rigid-boundary counterpart to dynamic windows. While dynamic windows (LC 209, LC 3, LC 76) grow and shrink adaptively based on constraint satisfaction, fixed-size windows enforce an immutable aperture width k=∣p∣k = |p|k=∣p∣ across the entire sequence.

🧭 Conceptual Foundation & Pattern Intuition

An anagram is any permutation of characters. The foundational insight is that two strings are anagrams if and only if their character frequency distributions are identical:

f⃗s=f⃗p  ⟺  substring s[L..R] is an anagram of p\vec{f}_s = \vec{f}_p \iff \text{substring } s[L..R] \text{ is an anagram of } pf​s​=f​p​⟺substring s[L..R] is an anagram of p

Because character arrangement inside the window is completely irrelevant, we never need to sort substrings or generate permutations. The algorithm only needs to verify whether the current kkk-length window matches ppp's character histogram.

A naive approach tests every candidate starting index i∈[0,∣s∣−∣p∣]i \in [0, |s| - |p|]i∈[0,∣s∣−∣p∣], taking a slice s[i..i+k−1]s[i..i+k-1]s[i..i+k−1] and counting or sorting its letters. Recomputing frequencies for kkk characters takes O(k)O(k)O(k) work per position. Across all (∣s∣−∣p∣+1)(|s| - |p| + 1)(∣s∣−∣p∣+1) windows, the total runtime degrades quadratically to:

Tnaive=O(∣s∣⋅∣p∣)T_{\text{naive}} = O(|s| \cdot |p|)Tnaive​=O(∣s∣⋅∣p∣)

When ∣s∣=30,000|s| = 30,000∣s∣=30,000 and ∣p∣=10,000|p| = 10,000∣p∣=10,000, this requires up to 3×1083 \times 10^83×108 operations—triggering a guaranteed Time Limit Exceeded (TLE) failure in technical interviews.

📐 Fixed-Width Viewport Mental Model

A fixed window of length k=∣p∣k = |p|k=∣p∣ maintains the strict invariant R−L+1=kR - L + 1 = kR−L+1=k. As the window shifts right by 1, only two characters change state: the exiting character on the left boundary is decremented, and the entering character on the right boundary is incremented.

Code / Blueprint
String s: | c | b | a | e | b | a | b | a | c | d |
Indices: 0 1 2 3 4 5 6 7 8 9
Target p: "abc" (len = 3) -> requires {a: 1, b: 1, c: 1}
 
Window 0: [ c b a ] -> freq: {a:1, b:1, c:1} == p -> MATCH! (Index 0)
slide +1: drop s[0]='c', add s[3]='e'
Window 1: [ b a e ] -> freq: {a:1, b:1, e:1} != p -> Miss
slide +1: drop s[1]='b', add s[4]='b'
Window 2: [ a e b ] -> freq: {a:1, b:1, e:1} != p -> Miss
...
Window 6: [ b a c ] -> freq: {a:1, b:1, c:1} == p -> MATCH! (Index 6)
 
🎞️ The Analogy: The Rigid Cardboard Stencil on a Conveyor Belt

Imagine an industrial conveyor belt carrying a continuous stream of letter tiles. Mounted directly above the belt is a stationary cardboard stencil with an open aperture window exactly kkk tiles wide.

When the conveyor belt rolls forward by one position:

  1. The k−1k - 1k−1 center tiles stay completely visible inside the aperture. Their counts do not change at all.
  2. Exactly one tile passes out of view on the left edge (outgoing = s[i - k]). We subtract it from our tally: window_count[outgoing] -= 1.
  3. Exactly one new tile rolls into view on the right edge (incoming = s[i]). We add it to our tally: window_count[incoming] += 1.

Re-counting all kkk tiles under the stencil on every step is redundant recomputation. By updating only the boundary delta (+1 incoming, -1 outgoing), state maintenance requires strictly O(1)O(1)O(1) constant operations per slide!

🔑 The Key Idea in the Code

There are two primary paradigms to manage and verify frequency equality:

Paradigm A: The Fixed 26-Element Array (O(Σ)O(\Sigma)O(Σ) Comparison)

Because strings consist of lowercase English letters, we allocate two 26-element arrays ([0] * 26):

Code / Blueprint
p_count, window_count = [0] * 26, [0] * 26
for ch in p:
p_count[ord(ch) - ord('a')] += 1
for ch in s[:k]:
window_count[ord(ch) - ord('a')] += 1 # First window: s[0..k-1]
 
result = [0] if window_count == p_count else []
for i in range(k, len(s)):
window_count[ord(s[i]) - ord('a')] += 1 # Add incoming s[i]
window_count[ord(s[i - k]) - ord('a')] -= 1 # Remove outgoing s[i - k]
if window_count == p_count: # O(26) = O(1) array equality check
result.append(i - k + 1) # Window s[i - k + 1..i]
 

Because the alphabet size Σ=26\Sigma = 26Σ=26 is constant, comparing the two 26-element arrays takes exactly 26 machine word comparisons—which is mathematically O(1)O(1)O(1). Total time is strictly O(∣s∣⋅26)=O(∣s∣)O(|s| \cdot 26) = O(|s|)O(∣s∣⋅26)=O(∣s∣).

Paradigm B: The FAANG O(1)O(1)O(1) Scalar matches Counter (Zero-Scan Optimization)

Can we evaluate whether the window is valid without even doing 26 comparisons per step? Yes. We maintain an integer register matches (from 000 to 262626), indicating how many of the 26 characters currently have matching counts in both sss and ppp.

  • On each slide, only two characters alter their frequencies: the incoming character and the outgoing character.
  • Before modifying a character's count, we check if it currently matches p_count. If modifying it breaks a match, we decrement matches -= 1.
  • After modifying its count, if it now equals p_count, we increment matches += 1.
  • The entire window is an anagram if and only if matches == 26! This evaluates window validity in strictly O(1)O(1)O(1) operations with zero array scanning, making it optimal even for large Unicode or ASCII alphabets.
⚖️ Brute Force vs. Fixed Sliding Window
DimensionBrute Force (Recompute Each Window)Fixed Sliding Window (Rolling Delta)
Time Complexity$O(s
Work per StepRe-scans entire kkk-character window (O(k)O(k)O(k) ops)Ingests 1 char, evicts 1 char (O(1)O(1)O(1) ops)
Auxiliary SpaceO(k)O(k)O(k) substring copiesO(1)O(1)O(1) fixed 26-element counters
Boundary InvariantUnmanagedRigid Aperture: R−L+1=kR - L + 1 = kR−L+1=k
💡 The 4-Step Fixed-Window Recipe for Interviews

Whenever an interview problem asks about a contiguous substring/subarray of fixed length kkk:

  1. Guard Condition: Immediately return empty/default if ∣s∣<k|s| < k∣s∣<k.
  2. Seed State: Process the first kkk elements into the window state accumulator (s[0..k-1]).
  3. Roll the Aperture: Iterate iii from kkk to ∣s∣−1|s|-1∣s∣−1. At each step:
    • Ingest s[i] into state.
    • Evict s[i - k] from state.
  4. Evaluate & Collect: Check state condition in O(1)O(1)O(1) and record the 0-indexed start position i - k + 1.
  • Recomputing frequency counts from scratch: Re-hashing the entire window in O(k) time degrades overall complexity to O(N * k). Maintain running counts with O(1) add and remove updates.

  • String length smaller than target: Failing to guard if len(s) < len(p): return [] causes out-of-bounds slice errors on initialization.

  • Comparing dictionary equality directly: In Python, comparing dict1 == dict2 takes O(26) = O(1) time, but in other languages maintain a scalar matches counter to avoid 26-key comparisons per step.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

Contiguous subarray/substring of fixed length K property verification.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: window contains exactly K elements at all times after the initial seed.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • When s[i] enters, s[i - k] leaves: removing s[i - k + 1] instead shifts every window, so the windows compared are the wrong ones.

  • Compare window_count == p_count only after both the add and the remove: in between, the window holds k + 1 letters and never matches.

  • if len(s) < k: return [] before building the first window: no window fits.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'd use a Fixed Window of size k, the length of p. I count p's letters into p_count and the first k letters of s into window_count. Then I slide one step at a time: s[i] enters and s[i - k] leaves, so I add one count and subtract one, and after both updates I compare window_count with p_count; if they're equal, the window starting at i - k + 1 is an anagram. The trap is the leaving index: it's s[i - k], and an off-by-one there shifts every window. Comparing two 26-entry lists is constant work, so it's O(N) time and O(1) extra space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

O(|s|): the slide loop runs once per letter of s after the first window, and each step does one add, one remove and one 26-entry comparison.

SPACE COMPLEXITY

O(1) Auxiliary

Look at memory allocations: The algorithm executes iteratively with O(1) stack frames. Auxiliary heap memory is strictly bounded by two fixed 26-element integer frequency arrays (or hash maps bounded by alphabet size Σ = 26). Since the alphabet size is constant regardless of input length, auxiliary space is strictly O(1) Auxiliary.

Formal Recurrence Relation

T(N)=∣p∣+(∣s∣−∣p∣)⋅Σ=O(∣s∣)T(N) = |p| + (|s| - |p|) \cdot \Sigma = O(|s|)T(N)=∣p∣+(∣s∣−∣p∣)⋅Σ=O(∣s∣)

Derivation Progression

Initial Seed Accumulation

O(|p|)

Constructs frequency counts for pattern p and the first |p| characters of s in O(|p|) time.

Rolling Delta Update

window_count[in] += 1; window_count[out] -= 1

Each slide adds the new incoming right character and subtracts the old left character in O(1) scalar operations.

Frequency Vector Comparison

O(Σ)=O(26)=O(1)O(\Sigma) = O(26) = O(1)O(Σ)=O(26)=O(1)

Checks whether window character counts match pattern counts in constant time.

Total Linear Pass

∣s∣−∣p∣ sliding steps|s| - |p| \text{ sliding steps}∣s∣−∣p∣ sliding steps

Overall execution time is strictly linear in string length: O(|s|).

Variable Definitions

∣s∣|s|∣s∣

Length of the target string s (up to 30,000)

∣p∣|p|∣p∣

Length of the pattern/anagram string p

ΣΣΣ

Alphabet cardinality (26 lowercase English letters)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative

🔵 Auxiliary Heap

O(1) Two 26-element frequency arrays

🟢 Output Space

O(|s|) Result start indices array

Boundary Best / Worst Cases

Best Case

O(∣s∣)O(|s|)O(∣s∣)

Average Case

O(∣s∣)O(|s|)O(∣s∣)

Worst Case

O(∣s∣)O(|s|)O(∣s∣)

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "find all start indices of p's anagrams in s", "both strings consist of lowercase English letters". Fixed-size sliding window (K=∣p∣K = |p|K=∣p∣) with rolling frequency state.

CONSTRAINTS & BOUNDS

1≤∣s∣,∣p∣≤3×1041 \le |s|, |p| \le 3 \times 10^41≤∣s∣,∣p∣≤3×104, lowercase English letters only. Recounting every window is O(∣s∣⋅∣p∣)O(|s| \cdot |p|)O(∣s∣⋅∣p∣), up to about 2.3×1082.3 \times 10^82.3×108 steps (when p is half as long as s); sliding the counts costs one add, one remove and one 26-entry comparison per step, about 8×1058 \times 10^58×105.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale: with a large alphabet (Unicode text instead of 26 letters) the full comparison costs O(∣Σ∣)O(|\Sigma|)O(∣Σ∣) per step. There, keep a matches counter of letters whose counts agree, change it only when the entering or leaving letter's count reaches or leaves its target, and test matches == len(p_count). On a stream, the window must also keep its last k characters (a ring buffer), or it cannot know which one leaves.

Core Algorithmic State Invariants

  1. Counts Are All an Anagram Needs

An anagram is the same letters in any order, so the order inside the window never matters: two strings are anagrams exactly when their 26 letter counts are equal. That is why window_count == p_count is a complete test, with no sorting and no pairing of letters.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•