38

Segment Trees, Fenwick & String Matching

Precompute a little structure once, then answer every query in O(log n) — or match a pattern without ever looking back.

advanced~90 min7 simulations7 problems

▶ guided lesson⚡ 1-minute summary↻ 15 recall cards

before this: Hashing · Tree Recursion · Bits · Arrays & Strings

01 · why it exists

Why this exists, and the weight it carries

since1989 · 1994

Fenwick tree (binary indexed tree)

Boris Ryabko (1989, 1992); Peter M. Fenwick, University of Auckland (1994)

Adaptive data compression (arithmetic coding) keeps a table of how often each symbol has appeared, and for every symbol it codes it must both update the table and read a running total. Boris Ryabko described this structure in 1989, in a paper on fast adaptive ("on-line") codes, with a further version in 1992. Peter Fenwick described it in 1994 as the "binary indexed tree": one array, where each operation's cost depends on the 1-bits of the index. His name stuck — today almost everyone calls it a Fenwick tree.

the problem it solvedKeep cumulative frequencies correct while they change on every symbol: update and prefix-sum both in O(log n), in a single array.

its jobs today

  • Adaptive compression, the job it was built for: an arithmetic coder updates one symbol's count and reads a running total for every symbol it encodes.
  • Live counts and ranks: "how many orders so far cost less than ₹500?" or a player's rank while scores keep changing — a Fenwick tree over compressed values answers each in O(log n).
  • Counting inversions — how far a list is from sorted, e.g. how much two rankings disagree — in O(n log n).

the weight it carries

Illustrative: 1 lakh (10⁵) values and 1 lakh calls, half updates and half range sums. Plain array: each sum may read 10⁵ values → 5·10⁴ × 10⁵ = 5·10⁹ steps. Prefix-sum array: each update rewrites up to 10⁵ prefixes → also 5·10⁹. Fenwick: ⌊log₂ 10⁵⌋ + 1 = 17 steps per loop → 5·10⁴ × 17 + 5·10⁴ × 2 × 17 = 2,550,000 — about 2,000× less, in one int[100001].

without it

Every "sum or count so far, while values keep changing" question would cost O(n) on one side — reads or writes — and slow down as the data grows.

when · why · how

when
Point updates mixed with prefix or range sums and counts; "how many smaller/greater values have I seen?" (with coordinate compression).
why
O(log n) per update and per query in about 10 lines and one O(n) array, versus O(1)/O(n) for a prefix array and O(n)/O(1) for a plain array. Ranges need an invertible operation (sum, count, XOR).
how
int[] tree = new int[n + 1]; update: for (int i = idx + 1; i <= n; i += i & -i) tree[i] += delta; prefix: for (int i = idx + 1; i > 0; i -= i & -i) sum += tree[i];

02 · plain english

The idea in 30 seconds

Range queries: store sums of blocks of the array, so any range is glued together from a few blocks instead of n single values.

Fenwick tree and segment tree keep those blocks fresh after updates in O(log n), where a prefix-sum array would need O(n).

String matching: when a comparison fails, reuse what you already matched (KMP, Z) or compare cheap fingerprints first (Rabin–Karp) instead of starting over.

picture this

A server dashboard never adds up a month of raw request logs when you open it. It keeps per-hour, per-day and per-week rollups, so "requests from the 3rd to the 20th" is a couple of weekly totals plus a few daily ones. A late correction to one hour only fixes that hour, its day and its week. Fenwick and segment trees are exactly these rollups, packed into an array.

03 · remember

What you must know

  • Prefix sums answer range sums in O(1): sum(l..r) = prefix[r+1] − prefix[l]. But one update forces O(n) rebuilding — fine only when the array never changes.
  • Fenwick tree (BIT): tree[i] holds the sum of the lowbit(i) = i & -i values ending at i (1-based). Query strips the lowest bit (i -= i & -i), update climbs (i += i & -i). Both O(log n), ~10 lines.
  • Segment tree: a binary tree where every node stores the answer for a segment. Any range splits into at most ~2 nodes per level, so query and update are O(log n). It works for any associative combine: sum, min, max, gcd.
  • Lazy propagation lets a segment tree add to a whole range in O(log n): stop at fully covered nodes and leave an IOU (lazy[node]) for the children.
  • Coordinate compression + Fenwick turns "how many earlier/later values are smaller?" into prefix-count queries over ranks — the engine behind counting inversions.
  • KMP prefix function: lps[i] = longest proper prefix of p[0..i] that is also its suffix. On a mismatch, jump to that border instead of restarting — the text pointer never moves back, so search is O(n + m).
  • Z-algorithm: z[i] = how far s[i..] matches the start of s. On pattern + '#' + text, any z[i] == m is a match. Also O(n + m).
  • Rabin–Karp: hash each window and slide the hash in O(1). Equal hashes must still be verified (collisions exist), so it's O(n + m) expected, O(n·m) worst case.

04 · see it

Play with it in 3D

Press Space to play, ←/→ to step, drag to move, ? for all shortcuts. Try your own input.

sim

Prefix sums recap

One O(n) pass, then any range is a single subtraction.

loading simulation…
sim

Fenwick tree: climb on update, strip bits on query

Each tree cell owns a block of nums (shown under it). Watch an update touch only log n cells.

loading simulation…
sim

Segment tree in 3D

Built bottom-up, then a query stops at green nodes (fully inside) and grey nodes (fully outside).

loading simulation…
sim

Lazy propagation: add to a range now, pay the children later

A range add stops at fully covered nodes and leaves an IOU. The IOU moves down only when a later add or query must look inside.

loading simulation…
sim

KMP prefix function (lps)

The matching prefix and suffix are highlighted. On a mismatch, len falls back through shorter borders.

loading simulation…
sim

KMP search — the pattern slides, i never goes back

The pattern row slides under the text by its own lps table.

loading simulation…
sim

Rabin–Karp rolling hash

Drop the left char, add the right char: the window hash updates in O(1).

loading simulation…

05 · cost

Time & space, explained

Range-sum structures (n = array length)
BuildPoint updateRange sumSpacewhy
Plain array, loop per queryO(n)O(1)O(n)O(n)Updates are a single write, but every query walks the whole range.
Prefix-sum arrayO(n)O(n)O(1)O(n)One subtraction per query, but changing nums[i] shifts every prefix after i.
Sqrt decompositionO(n)O(1)O(√n)O(n)√n chunks of √n values: a query sums ≤ √n chunks plus ≤ 2√n loose values.
Fenwick tree (BIT)O(n log n) or O(n)O(log n)O(log n)O(n)Each loop adds or strips one bit of i, so it runs at most ⌊log₂ n⌋+1 times. A smarter build pushes each node to its parent once → O(n).
Segment treeO(n)O(log n)O(log n)O(n) (4n array)2n−1 nodes built once. A query visits at most 4 nodes per level across ~log₂ n levels.
Segment tree + lazy propagationO(n)O(log n) per range addO(log n)O(n)A range add stops at O(log n) fully covered nodes and parks the rest in lazy[] until someone looks deeper.
String matching (text length n, pattern length m)
PreprocessSearchExtra spacewhy
Naive (try every start)—O(n·m) worstO(1)Text "aaaa…a" with pattern "aa…ab" re-compares almost all m characters at each of n starts.
KMPO(m)O(n)O(m)The text pointer only moves forward; each fallback undoes an earlier step, so total work ≤ 2n.
Z-algorithm on pattern#text—O(n + m)O(n + m)The right edge r of the Z-box only moves right; every comparison either moves r or ends a position.
Rabin–KarpO(m)O(n + m) expected, O(n·m) worstO(1)O(1) per slide; characters are only compared on hash equality, which is rare unless you're unlucky (or the input is adversarial).
String.indexOf (JDK)—O(n·m) worstO(1)The JDK uses a straightforward (heavily optimised) scan, not KMP — great in practice, quadratic in theory.

06 · java

Raw vs Collections Framework

Range sums: a hand-rolled prefix array vs the library

javaprefix array (exclusive, long)java.util.*
class PrefixRaw {    /** prefix[i] = a[0] + ... + a[i-1]; long avoids overflow when many ints add up. */    static long[] prefixSums(int[] a) {        long[] prefix = new long[a.length + 1];        for (int i = 0; i < a.length; i++) prefix[i + 1] = prefix[i] + a[i];        return prefix;    }     /** Sum of a[left..right], inclusive, in O(1). */    static long rangeSum(long[] prefix, int left, int right) {        return prefix[right + 1] - prefix[left];    }}
  • The extra leading 0 (prefix[0] = 0) removes every if (left == 0) special case.
  • Use long for prefix sums: 10⁵ values of 10⁹ overflow int long before you notice.
  • Arrays.parallelPrefix(arr, Long::sum) computes inclusive running sums in place. Handy, but interviewers usually want the 2-line loop.

Fenwick tree — the JDK has no equivalent

javareusable Fenwick (0-based API)java.util.*
class Fenwick {    private final long[] tree; // 1-indexed internally; callers use 0-based indices     Fenwick(int n) {        tree = new long[n + 1];    }     /** O(n) build: push each node's total to its parent once. */    Fenwick(int[] nums) {        this(nums.length);        for (int i = 1; i <= nums.length; i++) {            tree[i] += nums[i - 1];            int parent = i + (i & -i);            if (parent < tree.length) tree[parent] += tree[i];        }    }     void add(int index, long delta) {           // nums[index] += delta        for (int i = index + 1; i < tree.length; i += i & -i) tree[i] += delta;    }     long prefixSum(int index) {                 // nums[0] + ... + nums[index]        long sum = 0;        for (int i = index + 1; i > 0; i -= i & -i) sum += tree[i];        return sum;    }     long rangeSum(int left, int right) {        return prefixSum(right) - (left == 0 ? 0 : prefixSum(left - 1));    }}
  • There is no Fenwick or segment tree in java.util. You write them yourself — so practice until the ~15 lines are automatic.
  • TreeMap.headMap(k).size() looks like a rank query but is linear in the number of keys below k: the sub-map view counts by iterating. A Fenwick tree over compressed values does it in O(log n).
  • Keep the tree 1-indexed inside (i & -i is 0 at index 0, which would loop forever) and expose 0-based methods.
  • Integer.lowestOneBit(i) is the readable name for i & -i.

Lazy propagation: range add + range sum

javalazy segment treejava.util.*
class LazySegmentTree {    private final int n;    private final long[] tree; // tree[node] = sum of the node's segment    private final long[] lazy; // lazy[node] = an add still owed to both children     LazySegmentTree(int n) {        this.n = n;        tree = new long[4 * Math.max(1, n)];        lazy = new long[4 * Math.max(1, n)];    }     void rangeAdd(int left, int right, long val) { add(1, 0, n - 1, left, right, val); }     long rangeSum(int left, int right) { return query(1, 0, n - 1, left, right); }     private void apply(int node, int lo, int hi, long val) {        tree[node] += val * (hi - lo + 1); // every element in [lo, hi] grows by val        lazy[node] += val;                 // an IOU: the children still owe this add    }     private void push(int node, int lo, int hi) {        if (lazy[node] == 0) return;        int mid = (lo + hi) >>> 1;        apply(2 * node, lo, mid, lazy[node]);         // pay the IOU to both children        apply(2 * node + 1, mid + 1, hi, lazy[node]);        lazy[node] = 0;    }     private void add(int node, int lo, int hi, int left, int right, long val) {        if (right < lo || hi < left) return;                                  // no overlap        if (left <= lo && hi <= right) { apply(node, lo, hi, val); return; } // full overlap: stop, be lazy        push(node, lo, hi);                                                   // partial: settle the IOU first        int mid = (lo + hi) >>> 1;        add(2 * node, lo, mid, left, right, val);        add(2 * node + 1, mid + 1, hi, left, right, val);        tree[node] = tree[2 * node] + tree[2 * node + 1];                     // re-sum on the way up    }     private long query(int node, int lo, int hi, int left, int right) {        if (right < lo || hi < left) return 0;        if (left <= lo && hi <= right) return tree[node]; // already includes every add above it        push(node, lo, hi);                               // children must be up to date before we read them        int mid = (lo + hi) >>> 1;        return query(2 * node, lo, mid, left, right) + query(2 * node + 1, mid + 1, hi, left, right);    }}
  • apply updates a node and writes the IOU (lazy[node]); push pays the IOU to both children just before you visit them.
  • A node's tree value is always correct for its own segment; only its descendants may be behind. That's why a fully covered node can answer a query without pushing.
  • Allocate 4 * n: a recursive segment tree on n leaves can use indices up to just under 4n.
  • Swap + for Math.min / Math.max (and the identity 0 for ±∞) to get range-min / range-max trees. For range add + range max, apply adds val to the node's max (not val × length).

String search: KMP by hand vs `String` methods

javaprefix functionjava.util.*
class PrefixFunction {    /** lps[i] = length of the longest proper prefix of p[0..i] that is also a suffix of it. */    static int[] prefixFunction(String p) {        int[] lps = new int[p.length()];        for (int i = 1, len = 0; i < p.length(); i++) {            while (len > 0 && p.charAt(i) != p.charAt(len)) len = lps[len - 1];            if (p.charAt(i) == p.charAt(len)) len++;            lps[i] = len;        }        return lps;    }}
  • In a real codebase use indexOf / contains — they're fast in practice. In an interview, the algorithm is usually the question.
  • s.startsWith(p, offset) and regionMatches compare in place — no substring() copies (each copy is O(length)).
  • String.hashCode() is itself a polynomial hash (base 31) — the same idea as Rabin–Karp, but it can't be rolled.

Z-function

javaZ arrayjava.util.*
class ZFunction {    /** z[i] = length of the longest common prefix of s and s.substring(i); z[0] = 0 by convention. */    static int[] zFunction(String s) {        int n = s.length();        int[] z = new int[n];        for (int i = 1, l = 0, r = 0; i < n; i++) {      // [l, r) is the rightmost match box            if (i < r) z[i] = Math.min(r - i, z[i - l]);            while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) z[i]++;            if (i + z[i] > r) { l = i; r = i + z[i]; }        }        return z;    }}
  • Convention: z[0] = 0 (some books use n). Never rely on z[0].
  • Z and the prefix function carry the same information and can be converted into each other — learn one deeply, recognise the other.
Java API you will actually use
calldoescost
Arrays.parallelPrefix(long[] a, Long::sum)In-place inclusive running sumsO(n)
i & -i / Integer.lowestOneBit(i)Lowest set bit — the Fenwick step sizeO(1)
Arrays.stream(a).distinct().sorted().toArray()Sorted distinct values for coordinate compressionO(n log n)
Arrays.binarySearch(sorted, x)Rank of x after compression (index, ≥ 0 if present)O(log n)
s.indexOf(p) / s.indexOf(p, from)First match (from an offset); −1 if noneO(n·m) worst
s.contains(p)indexOf(p) >= 0O(n·m) worst
s.startsWith(p, offset)Does p occur exactly at offset? (no copy)O(m)
a.regionMatches(i, b, j, len)Compare two slices without substring()O(len)
new StringBuilder(s).reverse()Reversed copy (palindrome tricks)O(n)

Range sums, array never changes

→ Prefix-sum array

O(1) per query, trivial code.

Point updates + prefix/range sums

→ Fenwick tree

O(log n) both ways, smallest code, tiny constant.

Range min/max/gcd, or anything Fenwick can't invert

→ Segment tree

Works for any associative combine; Fenwick needs subtraction to get ranges.

Add to a whole range + range sums

→ Segment tree with lazy propagation

O(log n) range updates (or two Fenwicks if you know the trick).

Count smaller / greater elements seen so far

→ Coordinate compression + Fenwick

Rank queries in O(log n); merge sort is the alternative.

Find a pattern in text, guaranteed linear

→ KMP (or Z on pattern#text)

O(n + m) worst case, no hashing risk.

Compare many substrings for equality fast

→ Rabin–Karp / prefix hashes

O(1) per substring after O(n) preprocessing; verify on equal hashes.

07 · recognise

Spot the pattern

Many range-sum queries, no updates

Prefix sums (Hashing & Prefix Sums chapter) — don't overbuild

Range queries mixed with point updates, n and q up to ~10⁵

Fenwick tree (sums) or segment tree (min/max/anything)

"Add v to every element in [l, r]" plus range queries

Lazy segment tree (or difference array if there are no queries in between)

"How many elements to the left/right are smaller/greater?", inversions, reverse pairs

Coordinate compression + Fenwick, or merge-sort counting

Find a pattern in a string, or "longest prefix that is also a suffix"

KMP prefix function

Periodic / repeated strings, shortest palindrome by adding a prefix

Prefix function on a cleverly glued string (s + '#' + reverse(s))

"Longest duplicate substring", many substring equality checks

Binary search on length + Rabin–Karp hashing

08 · traps & tricks

Mistakes to avoid, tricks to use

traps

  • Fenwick with index 0: 0 & -0 == 0, so i += i & -i never moves — infinite loop. Shift to 1-based.
  • Fenwick update that sets a value: the tree stores sums, so add the difference val − nums[i] and keep a copy of nums.
  • Segment tree array of size 2n for a recursive tree: indices can exceed it. Use 4n.
  • Forgetting push before recursing in a lazy tree — children read stale sums.
  • Rabin–Karp without verifying on equal hashes: collisions will eventually give a wrong answer.
  • Negative values after modular subtraction: in Java % keeps the sign, so add MOD before the final % MOD (or use Math.floorMod).
  • KMP: resetting j = 0 on a mismatch instead of j = lps[j−1] while i keeps moving forward — that is not even the naive algorithm (which would move back), and it misses matches that start inside the failed attempt, like "aab" in "aaab".
  • Using int for big prefix sums or hashes: hash * BASE overflows int instantly — use long.

tricks

  • Memorise the Fenwick pair as a rhyme: query strips (i -= i & -i), update climbs (i += i & -i).
  • Say the segment-tree query as three cases out loud: no overlap → 0, full overlap → node value, partial → ask both children.
  • For KMP, write the prefix-function loop first; search is the same loop run over the text.
  • Before reaching for these structures, ask: are there updates? If not, prefix sums are enough and the interviewer will prefer them.
  • When stuck on a string problem, try gluing strings together (p + '#' + t, s + s, s + '#' + reverse(s)) and running a prefix/Z function on the result.
  • Test with: one element, all equal values, negatives, an update to the same index twice, pattern longer than text, pattern equal to text.

09 · why & how

Questions you might be too shy to ask

Why does i & -i give the lowest set bit?

In two's complement, -i = ~i + 1. Flipping all bits then adding 1 turns the trailing zeros back to zeros and the lowest 1 back to 1, while every higher bit is the opposite of i. AND keeps only that one shared bit. Example: 12 = 1100, −12 = …0100, so 12 & −12 = 0100 = 4.

Which values does tree[i] actually hold? Show me one.

tree[i] is the sum of the last i & -i values ending at position i (1-based). tree[7]: 7 & −7 = 1, so just position 7. tree[6]: 6 & −6 = 2, so positions 5–6. tree[4]: positions 1–4. So prefix(7) = tree[7] + tree[6] + tree[4]: three blocks glued together, not seven values. With n = 8, an update at 5 climbs tree[5] → tree[6] → tree[8] (5 + 1 = 6, 6 + 2 = 8), exactly the blocks that contain position 5.

Why is the range sum prefix(right + 1) - prefix(left)? Where did the +1 come from?

prefix(i) means "the sum of the first i values", i.e. indices 0..i−1. Indices 0..right are the first right + 1 values. Subtract the first left values (0..left−1), and what's left is exactly left..right. Example: left = 2, right = 4 → prefix(5) − prefix(2) = (0..4) − (0..1) = 2..4.

Fenwick or segment tree — which should I learn first?

Learn Fenwick for sums: it's ~10 lines and hard to get wrong. Learn the segment tree next because it handles min/max/gcd and range updates. If an interviewer just says "range sum with updates", either is a great answer.

Why can't a Fenwick tree do range minimum?

A Fenwick tree answers prefix queries. A range sum is prefix(right + 1) − prefix(left), which needs subtraction to cut away the part before left. Min has no "un-min", so you can't cut that part away. A segment tree combines exact blocks of the range, so it never needs to undo anything.

What is coordinate compression and when do I need it?

Replace each value by its rank among the sorted distinct values (e.g. [100, −7, 100, 42] → [3, 1, 3, 2]). Do it when values are huge or negative but you only care about their order — it shrinks the Fenwick tree to n slots.

I'll never remember segment-tree code under pressure. What do I actually memorise?

Only three facts: children of node are 2*node and 2*node+1; allocate 4*n; and the query's three cases (no overlap → identity, full overlap → tree[node], partial → combine both children). Build and update follow from those. Write it once from these facts every few days and it stops being scary — bas itna yaad rakho.

In lps, why must the prefix be proper?

Proper means "not the whole string itself". Every string is its own prefix and its own suffix, so without that rule lps[i] would always be i + 1 and tell you nothing. For "abab" the proper answer is 2 ("ab" at the start and at the end): on a mismatch after "abab", KMP keeps those 2 matched letters and continues from there.

Why does KMP run in O(n) when there's a while loop inside a for loop?

j goes up by at most 1 per character of the text (n times in total), and every fallback makes j strictly smaller. You can't go down more than you went up, so all fallbacks together cost at most n. That's amortised analysis, same as ArrayList growth.

KMP or Z — which one should I learn?

Both are O(n + m). Learn the prefix function (KMP) deeply: it answers borders, periods and search, and its table is only O(m). Recognise Z as the friendlier alternative when you can glue pattern + '#' + text and memory isn't tight.

Why the '#' in pattern + '#' + text for the Z-algorithm?

It's a wall. '#' appears in neither string, so no match can run across it. Then no z[i] can be bigger than m, and z[i] == m means exactly "the whole pattern starts here". Without the wall, a match could run from the pattern part into the text and give fake answers. Pick any character the input never uses.

Is Rabin–Karp safe to use in an interview?

Yes, if you verify the characters whenever the hashes match. Then the answer is always correct; only the running time depends on luck. Say it out loud: "O(n + m) expected; worst case O(n·m) if many collisions." Use a large prime modulus (1e9+7) and long arithmetic.

Rabin–Karp uses BASE = 131 and MOD = 1_000_000_007. Can I pick any numbers?

Almost, with two rules. The base should be bigger than every character code (131 is above all ASCII codes), so different strings give different polynomials before the mod. The modulus should be a large prime, so hash values spread out and collisions stay rare. Keep everything in long: a hash below 10⁹ times 131 is about 1.3·10¹¹, too big for int but easy for long.

Do interviews really ask segment trees?

Less often than arrays, graphs or DP. Candidates do report them in some hard rounds and online assessments (community interview reports, not official lists), and 307 or 315 are reported as follow-ups. The same reports mention KMP-style string questions (28, 214, 459) more often than segment trees.

10 · practice

Interview problems

Every problem has brute → better → optimal solutions in Java, with complexity and a dry run.

  1. #307
    Range Sum Query - Mutablemust-do
    MediumAdobeBloombergGoogleAmazon
  2. #315
    Count of Smaller Numbers After Selfmust-do
    HardGoogleBloombergMicrosoftAmazonMeta
  3. #28
    Find the Index of the First Occurrence in a Stringmust-do
    EasyTCSBloombergMicrosoftGoogleAmazon+1
  4. #459
    Repeated Substring Pattern
    EasyGoogleAmazonTCSBloombergMeta+1
  5. #1392
    Longest Happy Prefixmust-do
    HardAppleAmazonGoogleMicrosoftMeta
  6. #214
    Shortest Palindrome
    HardBloombergGoogleUberMicrosoftAmazon
  7. #1044
    Longest Duplicate Substring
    HardByteDance / TikTokGoogleAmazonGoldman SachsMeta

Also practise this here

Each problem has one home page in the course; these use this chapter's idea too. The note says what to look for.

  1. #303
    Range Sum Query - Immutable
    Easyin Hashing

    The static baseline: prefix sums answer range sums in O(1). Fenwick and segment trees exist to keep that speed when values change.

  2. #493
    Reverse Pairs
    Hardin Sorting

    Count Smaller's twin: walk right to left and count with a Fenwick tree over ranks — or count during merge sort.

11 · revise

The whole chapter on one screen

10 cards, about a minute. Read them the night before an interview, or right after the chapter to lock it in.

  1. what it is

    Range queries: keep block sums so any range is a few blocks. Strings: never re-read matched text (KMP, Z), or compare cheap hashes first (Rabin–Karp).

  2. use it when

    No updates → prefix sums. Point updates + sums → Fenwick. Min/max/gcd, or adds over a range → segment tree (+ lazy).

  3. template

    Fenwick (1-based): update climbs i += i & -i, query strips i -= i & -i. Range sum = prefix(r + 1) - prefix(l).

  4. template

    Segment-tree query: no overlap → identity, full overlap → tree[node], partial → both children (2*node, 2*node+1). Allocate 4 * n.

  5. template

    KMP: mismatch → j = lps[j - 1] (repeat); match → j++; j == m → match at i - m + 1. The text index i never moves back.

  6. cost

    Fenwick and segment tree: O(log n) per update and query, O(n) memory. KMP and Z: O(n + m). Rabin–Karp: O(n + m) expected, O(n·m) worst.

  7. trap

    Fenwick index 0 never moves (0 & -0 == 0) — stay 1-based. update sets a value: add val - nums[i], not val.

  8. trap

    Equal hashes are not equal strings: verify the characters, use long, and add MOD back after subtracting.

  9. use it when

    "How many smaller/greater values before or after i?" → compress values to ranks, walk once, count ranks with a Fenwick tree.

  10. pro tip

    Stuck on a string problem? Glue strings — p + '#' + t, s + s, s + '#' + reverse(s) — and run the prefix or Z function on the result.

12 · recall

Flip, remember, repeat

card 1 / 15
recall

Fenwick tree: what does i & -i give, and which way does each loop move?

tap or Space to flip ↻

Question 1: Fenwick tree: what does i & -i give, and which way does each loop move?

13 · check yourself

Quick quiz

  1. In a Fenwick tree, which indices does prefix(13) read? (13 = 1101₂)

  2. Why can't you answer range-min queries with a plain Fenwick tree?

  3. A segment tree query finds a node whose segment lies completely inside [left, right]. What does it do?

  4. In a lazy segment tree, when must you call push(node)?

  5. What is the lps (prefix function) array of "abab"?

  6. KMP has a while loop inside a for loop. Why is the search still O(n)?

  7. With the convention z[0] = 0, what is the Z array of "aabxaab"?

  8. In Rabin–Karp, two windows have equal hashes. What must you do?

  9. You need "add v to every element in [l, r]" and "sum of [l, r]", both 10⁵ times. Best tool?

  10. Why glue s + '#' + reverse(s) in Shortest Palindrome?