3.8.3 · D5String Algorithms
Question bank — Rabin-Karp — rolling hash, O(n+m) expected
Before we start, one word we lean on: a collision is when two different strings produce the same hash number. It is the villain of every question here.
True or false — justify
Two strings with equal hashes are guaranteed to be the same string
False. Equal hash is necessary but not sufficient — two different strings can collide, so you must still verify character-by-character.
Two strings with different hashes are guaranteed to be different strings
True. A correct hash is a function: identical inputs always give identical outputs, so unequal outputs force unequal inputs. This is why a hash mismatch lets us skip instantly.
Rabin-Karp is in the worst case
False. It is expected; the worst case is when nearly every window collides and we verify everywhere (e.g. a tiny prime or adversarial input).
Choosing a larger prime can never hurt correctness
True for correctness — it only lowers collision probability (~). It can hurt if is so large that overflows your integer type, so keep within a safe range (see Modular Arithmetic).
The rolling update lets us skip recomputing the whole hash each slide
True. Dropping the leading term, multiplying by , and adding the new term is , versus to rebuild — that single fact is the entire speedup.
Rabin-Karp can miss a genuine occurrence of the pattern
False. Equal strings always produce equal hashes, so a real match never fails the hash test. We only risk extra work (false positives), never missed matches.
Using smaller than the alphabet size is fine
False (dangerous). If is smaller than the number of distinct symbols, different digit strings map to the same base- number even before taking mod, inflating collisions. Pick alphabet size.
With double hashing (two independent hashes), you can safely skip verification
Mostly-safe but not proof. Collision probability drops to about , tiny enough for contests, but it is still a probability, not a guarantee. For an adversary-proof or provably-correct match, verify.
Spot the error
A student writes H = (H - T[i]*Phigh) * b + T[i+m] and skips % q. What breaks?
The number grows past , overflowing the integer type and giving garbage; the whole point of is to keep values bounded. Every step must reduce mod .
A student computes (H - T[i]*Phigh) % q and gets a negative number, then uses it directly. Error?
In most languages
% on a negative operand yields a negative result, which then never equals the pattern's hash (always in ), causing missed matches. Fix: ((x) % q + q) % q.A student recomputes Phigh = pow(b, m-1) % q inside the sliding loop. Error?
is a constant for the whole search, so recomputing it each iteration makes every step (or worse) and destroys the roll. Precompute it once.
A student picks "to keep arithmetic easy." Error?
A tiny prime causes collisions on almost every window, so verification runs everywhere and the algorithm degrades to . Use a large prime like .
A student maps 'a' -> 0 and hashes the whole alphabet from 0. Any subtle bug?
Mapping a character to means leading
'a's contribute nothing, so "a", "aa", "aaa" can share hash . Map characters to (or add 1) so length is encoded.A student reports a match as soon as h(P) == H_i, never comparing letters. Error?
That treats the hash as a proof when it is only a filter — a collision would report a fake match. Always run the character check on a hash hit.
A student uses H_{i+1} = (H_i * b - T[i]*b^m + T[i+m]) % q. Is this wrong?
Not wrong — it is an algebraically equal rearrangement (distribute the first), but it needs precomputed instead of , and still needs the add- trick. Both forms are fine if consistent.
Why questions
Why multiply by (not add) when rolling the window?
Every remaining character must shift one place higher in the base- number after we drop the leading digit — multiplying by raises every exponent by one at once, exactly like appending a digit shifts a decimal number left.
Why subtract specifically, not alone?
The leading character sits in the highest place value , so its contribution to the number is , not . Removing only would leave most of it behind.
Why a prime rather than any large number?
A prime spreads hash values more uniformly and avoids common factors between and that would collapse many strings to the same residue — see Modular Arithmetic for why shared factors cause clustering.
Why is the collision probability roughly , and what does that recall?
With a uniform hash into buckets, a random other string lands on the pattern's value with chance . This is the Birthday Paradox intuition: collisions become likely once you hash about strings.
Why does Rabin-Karp shine at multi-pattern search?
You can hash many patterns into a set once, then check each window's single hash against the set in — one text pass finds all of them, unlike Knuth-Morris-Pratt which is built around one pattern.
Why prefer Knuth-Morris-Pratt or Z-Algorithm when worst case matters?
They give guaranteed with no collision risk, whereas Rabin-Karp's guarantee is only expected — adversarial input can force .
Why does the same rolling idea power substring comparison?
Precomputing prefix hashes lets you extract any substring's hash in , so equality of arbitrary ranges becomes a number comparison — see String Hashing for Substring Comparison.
Edge cases
What happens when (empty pattern)?
An empty pattern matches at every position (including index ); most implementations special-case it since the hash of an empty string is and the window loop is degenerate.
What happens when (pattern longer than text)?
There are windows, so no window exists and the answer is "no matches." Guard the loop bound so it never runs.
What happens when (pattern same length as text)?
There is exactly one window (the whole text); you compute both hashes, compare, and verify once if they match — the sliding loop simply never iterates.
What if the pattern occurs at overlapping positions, e.g. in ?
Rabin-Karp reports every start index (0, 1, 2) because each window is hashed independently; overlaps are handled naturally, unlike some naive skips.
What if two identical characters give the whole text one repeated symbol, e.g. all 'a'?
Every window has the same hash, so every window triggers verification — a legitimate worst-case-ish scenario, but here the verifications also succeed, so it is genuinely -heavy yet correct.
What is the hash of a single-character string?
It is just that character's code times , i.e. its code mod — the polynomial collapses to one term, a useful sanity check for your code.
What if divides (e.g. )?
Then , so every character except the last contributes and only the trailing character affects the hash — massive collisions. Keep , easiest by using a prime .
Recall One-line summary of the traps
The hash is a fast filter, never a proof: verify on hits, keep values non-negative and reduced mod a large prime, precompute once, and remember the guarantee is expected, not worst-case.
Connections
- Parent: Rabin-Karp — the derivation these traps guard
- Hashing — where collisions come from
- Modular Arithmetic — negatives, primes, and
- Birthday Paradox — why collision chance
- Knuth-Morris-Pratt · Z-Algorithm — guaranteed linear alternatives
- String Hashing for Substring Comparison — same rolling idea, range queries