LC Patterns — Arrays & Strings

Prev: binary-search, two-pointers, sliding-window, 1d-dp · Code: C++
Per-pattern template: What it is → Intuition → Approach → Pitfalls → LC problems → Worked examples

◆ The Master Insight

Arrays & strings is the vocabulary topic. Most problems here aren't one giant algorithm — they're a small bag of representation tricks you combine: precompute totals (prefix sums), use a value as an index, turn different-looking things into one canonical form (sorted key), shuffle things in place (reversal/transpose), or carefully follow a spec (parsing). The real skill being tested is choosing the representation that makes the problem easy — then the code is short.

Route first — many "array problems" live in another file. Before reaching here, ask:
  • Pair-finding with order, or squeezing an array in place → two-pointers
  • A contiguous subarray with a constraint (longest/shortest/count) → sliding-window
  • Sorted data, or a yes/no test that flips once → binary-search
  • Choices with overlapping subproblems → 1d-dp
What's left — and belongs here — is precomputation, index/value tricks, in-place rearrangement, intervals, hashing/canonical keys, and string simulation.
The one recurring idea in this file: spend one O(n) pass preparing so that every later query is O(1) (prefix sums, difference arrays, hash maps). Same "do the work once, serve fast" idea as caching — at array scale.

§0C++ Notes & Universal Pitfalls

The traps that cost wrong answers

1. Prefix-sum overflow. With n=10⁵ values up to 10⁹, a running total reaches 10¹⁴ — far past int. Make prefix arrays long long from the start.

2. size_t underflow. v.size()-1 on an empty vector wraps to a giant positive number and crashes. Guard with (int)v.size()-1. C++-only trap.

3. Off-by-one in prefix indexing. Use size n+1 with pre[0]=0 and pre[i]=pre[i-1]+a[i-1]; sum of [l,r] is pre[r+1]-pre[l]. Write it as a comment; never improvise mid-loop.

4. String building cost. s = s + t in a loop is O(n²). Use s += t and reserve(). C++ std::string is already mutable — no StringBuilder needed.

5. Mutating while iterating. Erasing from a vector in a forward loop skips elements. Iterate backwards or build fresh.

6. The edge triad. Run every solution against three inputs: empty, single element, all-identical. This topic hides most bugs in exactly these three.

1Prefix Sums (and Prefix/Suffix Products)

What it is: Add up the array once into a "running total" array, so any range sum [l,r] becomes one subtraction: pre[r+1] − pre[l]. The product version: "product of everything except me" = (product to my left) × (product to my right) — two passes, no division.

Range Sum — a = [2, 4, 1, 3, 5], query sum of a[1..3]
array a
2
0
4
1
1
2
3
3
5
4
└──── 4 + 1 + 3 = 8 ────┘
prefix pre (size n+1, pre[0]=0)
0
0
2
1
6
2
7
3
10
4
15
5
sum(a[1..3]) = pre[4]pre[1] = 10 − 2 = 8  → every query is O(1) after the one-time build
prefix total the two we subtract range being summed

Intuition: A range sum is the difference of two running totals — everything up to r, minus everything before l. Works for any operation that can be "undone" (sum and XOR both can). When it can't be undone cleanly (products with a zero, or min/max), switch to the two-sided version: build a total from the left and one from the right, and combine.

C++ · Product of Array Except Self (LC 238)
vector<int> productExceptSelf(vector<int>& a) {
    int n = a.size();
    vector<int> res(n, 1);
    for (int i = 1; i < n; ++i) res[i] = res[i-1] * a[i-1];   // res[i] = product of everything to the left
    long long suf = 1;
    for (int i = n - 1; i >= 0; --i) { res[i] *= suf; suf *= a[i]; }  // multiply in the right side
    return res;
}
Product Except Self — a = [1, 2, 3, 4], two passes, no division
pass 1 → : res = product of everything to the LEFT
1
1
2
6
pass 2 ← : multiply in suffix product (everything to the RIGHT)
24
×24
12
×12
8
×4
6
×1
each spot = (left product) × (right product), never touching its own value → [24, 12, 8, 6]

Challenges & pitfalls

The n+1 indexing convention (§0.3); overflow (§0.1); noticing XOR versions (same trick, ^ instead of +, since XOR undoes itself); and remembering 238 bans division on purpose — the division shortcut dies the moment a zero appears.

LC 303 Range Sum QueryLC 238 Product Except SelfLC 724 Find Pivot Index
Example 1 · Range Sum Query (LC 303) — a = [2,4,1,3,5]
  • Build running total, one longer than the array, starting at 0: pre = [0,2,6,7,10,15].
  • Sum of a[1..3] = pre[4] − pre[1] = 10 − 2 = 8. Check: 4+1+3 = 8.
  • After the one-time build, every query is O(1) — no matter how many they ask.
Example 2 · Product Except Self (LC 238) — a = [1,2,3,4]
  • Pass 1 (left→right): product to the left of each spot → res = [1,1,2,6].
  • Pass 2 (right→left): carry running product suf and multiply in → [24,12,8,6].

2Prefix Sum + Hash Map (first-occurrence trick)

What it is: Count (or find) subarrays whose sum is exactly k — even with negatives, where sliding windows fail. Walk once keeping a running sum. A subarray [j+1..i] sums to k exactly when pre[i] − pre[j] = k. So at each step ask the map: "have I seen an earlier running-sum equal to pre[i] − k?"

Intuition: This turns a "which two endpoints?" search into a "walk forward and remember what I've seen" scan. It's literally Two Sum, played on running sums. 525 turns each 0 into −1 so "equal 0s and 1s" becomes "sum = 0"; 974 stores the running sum mod k; "longest such subarray" versions store the first index a running-sum appeared at.

C++ · Subarray Sum Equals K (LC 560)
int subarraySum(vector<int>& a, int k) {
    unordered_map<long long, int> seen{{0, 1}};   // "empty prefix" seen once — lets subarrays starting at index 0 count
    long long pre = 0; int cnt = 0;
    for (int x : a) {
        pre += x;
        if (auto it = seen.find(pre - k); it != seen.end()) cnt += it->second;  // ask before recording
        ++seen[pre];
    }
    return cnt;
}
LC 560 — a = [1, 2, 3], k = 3. Walk once; ask the map before recording.
seed the map with the empty prefix → { 0 : 1 }
see xprelook for pre − kfound?cnt
111 − 3 = −20
233 − 3 = 0yes (×1)1
366 − 3 = 3yes (×1)2
answer 2 → subarrays [1,2] and [3]. The {0:1} seed is what lets [1,2] (starts at index 0) count.

Challenges & pitfalls

The {0,1} seed (leave it out and you silently miss every subarray starting at index 0 — the #1 bug); always ask the map before inserting the current sum; C++ % on a negative is negative, so normalize with ((x % k) + k) % k in 974; store a count vs a first index depending on "how many" vs "longest".

LC 560 Subarray Sum Equals KLC 525 Contiguous ArrayLC 974 Subarray Sums Divisible by K
Example 2 · Contiguous Array (LC 525) — a = [0, 1, 0]
  • Replace each 0 with −1: "equal 0s and 1s" becomes "sum = 0." Array is conceptually [−1, 1, −1].
  • Store the first index each running sum appears at. Seed: sum 0 first appears at index −1.
  • i=1: sum 0, seen at −1 → length 1 − (−1) = 2. Best = 2 (subarray [0,1]). Storing first occurrence maximizes the span.

3Difference Array (batch range updates)

What it is: The mirror image of prefix sums. For many updates "add v to every element in [l,r]," don't touch every element — record the two edges: diff[l] += v and diff[r+1] -= v. One prefix-sum pass turns the edges back into final values. So m updates + one pass = O(n + m) instead of O(n·m).

Intuition: A range update changes nothing in the middle — only the two boundaries (where the added value switches on and off). Store where things change, and "add it all up" at the end. It's an event timeline: car-pooling is "+v get on at l, −v get off at r."

C++ · Corporate Flight Bookings (LC 1109)
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
    vector<long long> diff(n + 1, 0);
    for (auto& b : bookings) { diff[b[0]-1] += b[2]; diff[b[1]] -= b[2]; }  // 1-indexed problem → shift to 0-indexed
    vector<int> res(n);
    long long run = 0;
    for (int i = 0; i < n; ++i) { run += diff[i]; res[i] = run; }   // add up the edges
    return res;
}
LC 1109 — n=3, bookings [1,2,+10] and [2,3,+20]. Mark edges, sweep once.
record only the edges into diff (size n+1)
+10
0
+20
1
−10
2
−20
3
sweep = running prefix sum → seats per flight
10
f1
+20 →
30
f2
−10 →
20
f3
answer [10, 30, 20] — flight 2 in both bookings → 10+20=30. Only 2 writes per booking, never looped the range.
+v start edge −v just past end swept result

Challenges & pitfalls

The right edge is exclusivediff[r+1], which is why the array is size n+1 (this off-by-one is the whole pattern); watch 1-indexed statements vs your 0-indexed array; recognizing the pattern at all ("lots of range additions, read at the end"); and if reads and updates are interleaved, this breaks — that's Fenwick/segment-tree territory (name it, don't build it unless asked).

LC 1109 Corporate Flight BookingsLC 1094 Car PoolingLC 2381 Shifting Letters II
Example 2 · Car Pooling (LC 1094) — trips [2,1,5], [3,3,7], capacity 4
  • Treat the road as a timeline. [2,1,5]: diff[1]+=2, diff[5]-=2. [3,3,7]: diff[3]+=3, diff[7]-=3.
  • Sweep: at location 1 → 2 aboard; at location 3 → 2+3 = 5 aboard.
  • 5 > capacity 4 → return false.

4Index Marking / Cyclic Sort (array as its own hash map)

What it is: When values are bounded by the array's size (values in [1, n]), the array acts as its own lookup table. Mark "value v present" by changing position v−1 (e.g. flip its sign), or cyclic-sort each value to its home slot so a[i] == i+1. O(n) time, O(1) extra space — the follow-up that rules out "just use a hash set."

Intuition: A value→info map costs O(n) memory. But if values fit in [1,n], the array's own indices are that key space — the slot tells you the value, and the sign bit (or swap position) is free storage you already own. In First Missing Positive, once every value is home, the first slot not holding its expected value points straight at the answer.

C++ · First Missing Positive (LC 41)
int firstMissingPositive(vector<int>& a) {
    int n = a.size();
    for (int i = 0; i < n; ++i)
        while (a[i] >= 1 && a[i] <= n && a[a[i]-1] != a[i])
            swap(a[i], a[a[i]-1]);            // send a[i] to its home slot
    for (int i = 0; i < n; ++i)
        if (a[i] != i + 1) return i + 1;      // first slot that isn't holding its own value
    return n + 1;
}
LC 41 — a = [3, 4, −1, 1]. Send each value to slot value−1, then scan.
start
3
0
4
1
−1
2
1
3
after cyclic-sort (each value swapped home; out-of-range values ignored)
1
✓ 1
−1
want 2
3
✓ 3
4
✓ 4
scan for first slot not holding i+1: slot 1 holds −1, expected 2 → answer 2
value at home slot first wrong slot → the answer out of range / noise

Challenges & pitfalls

The swap guard a[a[i]-1] != a[i] stops an infinite loop on duplicates — the bug of this pattern; be ready to explain why while-swap is still O(n) ("each swap puts one value in its final home, so ≤ n swaps"); if you sign-flip instead, read abs(a[i]) everywhere; mutating destroys the input — ask if that's allowed.

LC 448 Find All DisappearedLC 442 Find All DuplicatesLC 41 First Missing Positive
Example 2 · Find All Numbers Disappeared (LC 448) — a = [4,3,2,7,8,2,3,1]
  • For each value v, go to slot v−1 and flip that number negative to mark "v showed up."
  • After marking, any slot still positive was never visited — its index+1 is missing.
  • Slots for 5 and 6 stay positive → missing = [5, 6]. No hash set needed.

5In-Place Rearrangement via Reversal / Transpose

What it is: Rotating an array by k, rotating a matrix 90°, reversing words — all done in place by combining simple reversals. Rotate-by-k = reverse the whole thing, then reverse the first k, then the rest. Rotate a matrix = transpose, then reverse each row. Reverse words = reverse the whole string, then reverse each word back.

Rotate Array (LC 189) — a = [1..7], rotate right by k = 3, via three reversals
reverse the whole array
7
6
5
4
3
2
1
reverse the first k = 3
5
6
7
4
3
2
1
reverse the rest
5
6
7
1
2
3
4
answer [5,6,7,1,2,3,4] — the last 3 wrapped to the front. All in place, no extra array.

Intuition: Reversing undoes itself and needs no extra space, and rotations/word-swaps break down neatly into a few reversals. Don't trust the formula blindly — follow one element: track where a[0] ends up after each reversal; if it lands where it should, the whole thing is right.

C++ · Rotate Array + Matrix 90°
void rotate(vector<int>& a, int k) {
    int n = a.size(); k %= n;
    reverse(a.begin(), a.end());
    reverse(a.begin(), a.begin() + k);
    reverse(a.begin() + k, a.end());
}
// Matrix 90° clockwise: transpose (swap a[i][j], a[j][i] for j > i), then reverse each row.
Rotate Image (LC 48) — 3×3, 90° clockwise = transpose, then reverse each row
1
2
3
4
5
6
7
8
9
transposeswap j>i
1
4
7
2
5
8
3
6
9
reverseeach row
7
4
1
8
5
2
9
6
3
follow corner 1: top-left → (transpose) still top-left → (reverse row) top-right — exactly where a clockwise turn sends it.

Challenges & pitfalls

Do k %= n first (if k > n the naive version breaks); in transpose, only swap for j > i (the full range swaps everything back); counter-clockwise is transpose + reverse columns — derive it by tracking a corner, don't memorize; reversing words also means collapsing multiple spaces (in 151, the space cleanup is the actual hard part).

LC 189 Rotate ArrayLC 48 Rotate ImageLC 151 Reverse Words in a String

6Matrix Traversal & In-Place State Encoding

What it is: Two related skills. (a) Walking a matrix in an unusual order (spiral, diagonal) by tracking shrinking boundaries. (b) Updating a matrix in place when a new value depends on old neighbors — done by storing both old and new value in one cell (an extra bit or sentinel), decoding in a second pass.

Spiral Matrix (LC 54) — four edge-walks, boundaries closing inward
1
2
3
4
5
6
7
8
9
output order:
top → 1 2 3   right ↓ 6 9
bottom ← 8 7   left ↑ 4
center 5
= [1,2,3,6,9,8,7,4,5]

Intuition: (a) A spiral is four edge-walks — top → right → bottom → left — the boundary shrinking after each side. The loop structure is the answer; simulate it. (b) Game of Life has a chicken-and-egg problem: read old values while writing new. It goes away if one cell holds both — store old + 2·new (bit 0 = old, bit 1 = new), then a final >> 1 pass reveals the new grid. Same idea as 73's trick: use the first row/column as scratch space.

C++ · Spiral skeleton
int top = 0, bot = m - 1, lef = 0, rig = n - 1;
while (top <= bot && lef <= rig) {
    for (int j = lef; j <= rig; ++j) out.push_back(a[top][j]);   ++top;   // top row, →
    for (int i = top; i <= bot; ++i) out.push_back(a[i][rig]);   --rig;   // right col, ↓
    if (top <= bot) for (int j = rig; j >= lef; --j) out.push_back(a[bot][j]); --bot;  // bottom row, ←
    if (lef <= rig) for (int i = bot; i >= top; --i) out.push_back(a[i][lef]); ++lef;  // left col, ↑
}
Set Matrix Zeroes (LC 73) — use first row/col as notepads, O(1) extra space
1
1
1
1
0
1
1
1
1
mark edgesa[1][0], a[0][1]
1
0
1
0
0
1
1
1
1
apply
1
0
1
0
0
0
1
0
1
the 0 at (1,1) marks its row & column in the edges; second pass zeroes them. Reused the matrix's edges as memory.

Challenges & pitfalls

The two mid-loop re-checks (if (top<=bot) and if (lef<=rig)) — without them a leftover single row/column prints twice (the classic spiral bug); 73's subtlety is the first column needs its own separate flag, since [0][0] can't mark both first-row and first-column at once; encoding tricks must stay decodable (% 2, >> 1) and be explained aloud.

LC 54 Spiral MatrixLC 73 Set Matrix ZeroesLC 289 Game of Life

7Intervals: Sort, Then Sweep

What it is: Problems about ranges [start, end] — merge overlapping ones, insert a new one, or count how many to delete so none overlap. Almost all yield to: sort by start (or by end for greedy-deletion), then one linear pass carrying the "current" interval, deciding at each step: overlap, or start a new group?

Merge Intervals (LC 56) — [1,3] [2,6] [8,10] [15,18]
1–3
2–6
8–10
15–18
merged ↓
1–6 (merged)
8–10
15–18
1
3
6
8
10
15
18
sorted by start, [1,3] and [2,6] overlap (3 ≥ 2) → extend end to max(3,6)=6. answer [[1,6],[8,10],[15,18]]

Intuition: Once sorted by start, an interval can only overlap the one right before it — so cur.end ≥ next.start is the complete overlap test, and merging is cur.end = max(cur.end, next.end). The greedy cousin (435): to keep the most non-overlapping intervals, sort by end and always keep the earliest finisher — it leaves the most room. Overlap test to memorize: two intervals overlap when max(s1,s2) ≤ min(e1,e2).

C++ · Merge Intervals (LC 56)
vector<vector<int>> merge(vector<vector<int>>& iv) {
    sort(iv.begin(), iv.end());                       // by start (then end)
    vector<vector<int>> out;
    for (auto& in : iv) {
        if (!out.empty() && out.back()[1] >= in[0]) out.back()[1] = max(out.back()[1], in[1]);  // overlap → extend
        else out.push_back(in);                                                                  // gap → new group
    }
    return out;
}

Challenges & pitfalls

Touching endpoints — is [1,2],[2,3] an overlap? ( vs > — read it, state your reading aloud); sort-by-start (merge) vs sort-by-end (greedy count) — picking wrong almost works, so the bug is sneaky; 57 without re-sorting is three phases; the "minimum meeting rooms" variant escalates to an event sweep or min-heap — recognize when you've crossed that line.

LC 56 Merge IntervalsLC 57 Insert IntervalLC 435 Non-overlapping Intervals
Example 2 · Non-overlapping Intervals (LC 435) — [1,2] [2,3] [3,4] [1,3]
  • Sort by end: [1,2] [2,3] [1,3] [3,4].
  • Keep [1,2]; [2,3] no overlap → keep; [1,3] starts at 1 < 3 → overlaps → remove (count 1); [3,4] no overlap → keep.
  • Removed 1. Keeping the earliest finisher minimizes removals.

8Canonical-Key Hashing & Grouping

What it is: Group or compare things that are "the same after some transformation" — anagrams, isomorphic strings, mirrored patterns — by turning each item into a single canonical key (sorted letters, a letter-count signature, a first-occurrence encoding) and hashing on that key. Equivalent items produce the same key, so they land in the same bucket automatically.

Group Anagrams (LC 49) — sorted-letters key collides equivalent words
words → key
eat → aet
tea → aet
tan → ant
ate → aet
nat → ant
bat → abt
bucket
buckets
aet
eat, tea, ate
ant
tan, nat
abt
bat
rearrangements produce the identical key → grouping is automatic. Faster key: a 26-slot count array, O(L) beats O(L log L).

Intuition: Pairwise "same-except-for-rearrangement" is slow (O(n²)). But normalize each item to one standard form and equal items collide for free. Isomorphic strings / word patterns → encode each char by the index where it first appeared ("egg" → 0,1,1; "add" → 0,1,1 → match ⇒ isomorphic). First-occurrence encoding automatically enforces the two-way matching an isomorphism needs.

C++ · Group Anagrams (LC 49)
vector<vector<string>> groupAnagrams(vector<string>& strs) {
    unordered_map<string, vector<string>> g;
    for (auto& s : strs) {
        string key(26, 0);
        for (char c : s) ++key[c - 'a'];      // the letter-count signature is the canonical key
        g[key].push_back(s);
    }
    vector<vector<string>> out;
    for (auto& [k, v] : g) out.push_back(move(v));
    return out;
}

Challenges & pitfalls

An isomorphism must hold in both directions ("ab"→"aa" fails only on the reverse mapping — first-occurrence encoding sidesteps this); count-array keys must be genuinely hashable (a string of counts works; text counts need separators — "1,11" must not collide with "11,1"); don't hardcode a 26-letter alphabet without checking (Unicode? uppercase?).

LC 49 Group AnagramsLC 242 Valid AnagramLC 205 Isomorphic Strings
Example 2 · Isomorphic Strings (LC 205)
  • "egg" → e=0, g=1, g=1 → [0,1,1]. "add" → a=0, d=1, d=1 → [0,1,1]. Same → isomorphic.
  • "foo" → [0,1,1]. "bar" → b=0, a=1, r=2 → [0,1,2]. Different → not isomorphic.

9String Parsing & Simulation

What it is: No clever algorithm — just a spec you implement exactly: atoi (skip whitespace, read a sign, read digits, clamp on overflow), comparing version numbers (split, compare number by number, missing parts count as 0), multiplying big numbers digit by digit. Graded on careful edge-case handling and clean state, not cleverness.

atoi (LC 8) — parse "   -42abc" as ordered phases
4
2
a
b
c
1 · skip whitespace 2 · read sign 3 · read digits → 42 4 · stop at non-digit, ignore
apply sign → −42. Overflow case "2147483648": guard res > (INT_MAX − d)/10 fires before the multiply → clamp to INT_MAX.

Intuition: These test whether you can turn an English spec into ordered steps without dropping a case. Method: (1) restate the spec as a numbered list of phases; (2) list edge inputs straight from the spec ("", " ", "+-2", "words 42", overflow "2147483648"); (3) code one phase at a time. Overflow idiom: check res > (INT_MAX − digit) / 10 before multiplying. Multiply-strings: digits at i and j land in result position i + j + 1 (carry into i + j).

C++ · The overflow guard (memorize this)
int res = 0;
while (i < n && isdigit(s[i])) {
    int d = s[i] - '0';
    if (res > (INT_MAX - d) / 10)             // would overflow on the next step → clamp per spec
        return sign == 1 ? INT_MAX : INT_MIN;
    res = res * 10 + d;  ++i;
}

Challenges & pitfalls

Coding before listing edge cases (the spec is the test suite); the overflow check must go before the multiply, always; leading zeros in multiply-strings ("0"×"0"="0"); implicit zeros in version compare ("1.0" equals "1.0.0"); narrate the phases as you code — silent simulation is impossible for an interviewer to follow.

LC 8 String to Integer (atoi)LC 165 Compare Version NumbersLC 43 Multiply Strings
Example 2 · Compare Version Numbers (LC 165)
  • "1.01" vs "1.001": split on ., compare as integers → 1=1, then 01→1 = 001→1. All equal → 0.
  • "1.0" vs "1.0.0": first two equal; missing third part treated as 0, and 0==00, they're equal.

10Hash-Set Membership Tricks (complement lookup & smart starts)

What it is: One pass with an unordered_set/unordered_map replacing a nested loop. Two Sum asks the map "have I seen target − x?" Longest Consecutive Sequence (128) gets O(n) on unsorted data by only starting to count a run at its head — a number x where x−1 is not in the set.

Longest Consecutive Sequence (LC 128) — a = [100, 4, 200, 1, 3, 2]
put in a set; only a head (no predecessor in set) starts a walk
100
head, len 1
200
head, len 1
1
head ↓
2
walk
3
walk
4
walk
1 is a head (0 absent) → walk 1·2·3·4 = length 4. 2,3,4 have predecessors → skipped as heads. Each element walked once → O(n).
head, isolated run the length-4 run

Intuition: A hash lookup is basically a free inner loop. The deeper trick in 128 is accounting for the work: walking x, x+1, x+2, … looks quadratic, but only sequence heads start a walk, and every element is walked once across the whole run → amortized O(n). "Only start at heads" is a general de-dup idea: make each piece of work belong to exactly one owner. (Two Sum here vs Two Sum II: unsorted + need indices → hash map; already sorted → two pointers.)

C++ · Longest Consecutive Sequence (LC 128)
int longestConsecutive(vector<int>& a) {
    unordered_set<int> s(a.begin(), a.end());
    int best = 0;
    for (int x : s) {
        if (s.count(x - 1)) continue;          // not a head — someone else's run counts this one
        int y = x;
        while (s.count(y + 1)) ++y;            // walk the run forward
        best = max(best, y - x + 1);
    }
    return best;
}

Challenges & pitfalls

Iterate the set, not the array (array duplicates re-run walks and break the O(n) argument); be ready to defend the amortized bound aloud (interviewers push there); unordered_* is O(n) worst-case per op with a bad hash — mention that, or reserve(); in Two Sum, query before you insert, or an element matches itself when i == j.

LC 1 Two SumLC 128 Longest ConsecutiveLC 36 Valid Sudoku
Example 1 · Two Sum (LC 1) — a = [2,7,11,15], target 9
  • Map value→index. See 2 (i=0): need 7, absent → store 2→0.
  • See 7 (i=1): need 2, in map at index 0 → answer [0,1]. Querying before inserting stops a number pairing with itself.

Recognition Cheat Sheet

#PatternTrigger phraseCore trick
1Prefix sums/products"range sum", "product except self"range query = subtract two running totals
2Prefix + hash map"subarray sum exactly k", negatives presentlook up the complementing prefix, seed {0:1}
3Difference array"add v to range [l,r], many times"record edges, add up once
4Index marking / cyclic sort"values in [1,n]", "O(1) space", "missing/duplicate"array indexes itself; swap each value home
5Reversal composition"rotate in place", "reverse words"rotation = three reversals; 90° = transpose + reverse
6Matrix traversal/encoding"spiral", "in place" with neighbor readsshrinking boundaries; pack two states in one cell
7Intervals"merge/insert/remove overlapping"sort by start (merge) or end (greedy), then sweep
8Canonical-key hashing"group anagrams", "isomorphic"normalize each item to one standard key
9Parsing/simulation"implement atoi", "compare versions"spec → phases; overflow guard before multiply
10Hash-set membership"two sum", "longest consecutive", O(n) unsortedcomplement lookup; only start runs at heads

Study Order & The Interview Sentence

12 (precompute pair — 2 is the escape hatch for negatives) → 1078 (the hashing trio) → 4 (the O(1)-space follow-up killer) → 5639 (edge-case discipline — drill last, and often)
"The representation that makes this easy is ⟨prefix sums / the array indexing itself / a canonical key⟩ — one O(n) preparation pass, then each ⟨query / element⟩ is O(1). Let me state the indexing convention before I code."