Rabin-Karp is quoted as expected O(n+m) — what input forces O(n*m), and how do you defend against it?
answer
- expected over what, exactly
- who supplies the text in your system
- what happens when every offset is a hit
- fixed public parameters leave no randomness
- randomize the base, widen the range, cap confirmations
basics
~20 sAny input that makes a fingerprint hit fire at nearly every offset forces an O(m) confirmation each time, giving O(n*m). Either an attacker crafts collisions against your fixed hash parameters, or the pattern genuinely occurs almost everywhere.
solid answer
~50 sThe expected bound assumes spurious hits are rare, and that assumption is an input-and-parameters claim, not a guarantee. If the base and modulus are fixed and public, an adversary supplying the text can construct a window at every offset that collides with the pattern's fingerprint; every offset then pays an O(m) confirmation and the scan is Θ(n*m). A benign version exists too: if the pattern genuinely occurs at a constant fraction of offsets, verification is real, unavoidable work. Defences target the first case. Draw the base — and ideally the modulus — at random per process from a large family and never expose it, so precomputed collisions cannot be aimed at you. Widen the range so the accidental hit rate n/M is negligible. Instrument confirmations per scan, and above a threshold bail out to a matcher with a worst-case linear guarantee or reject the input outright.
go deeper
Know that Rabin-Karp's headline bound is an expected one and that its worst case is O(n*m). Be able to say what has to happen at every offset for the worst case to appear.
Explain where the randomness in an expected bound lives — the hash parameters, not the input — and why fixed, public parameters remove it and hand the worst case to whoever supplies the text.
Diagnose and harden: randomized per-run parameters, a wide range, a confirmation counter with a bail-out, size caps, and a metric that moves before the latency graph does.
Decide whether a probabilistic bound is acceptable on an untrusted path at all, what the fallback matcher is, and who owns the incident when a cheap request turns into quadratic CPU.
## What "expected" is actually quantifying Expected O(n+m) is a statement about the *number of spurious confirmations*: it assumes that, across n offsets, only a constant number of fingerprint hits are collisions rather than matches. Where does the randomness come from? Not from the text — the text is whatever it is. It comes from the choice of hash parameters. When the base and modulus are drawn from a large family, the probability that any specific pair of unequal windows collides is small, so over the whole scan the expected spurious count is roughly n/M. That wording matters because expected is not worst case and not amortized. An expected bound averages over your own coin flips; it promises nothing about any single scan and, crucially, it collapses entirely if the parameters are fixed and known — then there are no coin flips left to average over, and an adversary can choose the input after seeing them. ## Route one to the worst case: adversarial collisions Suppose your service exposes a search where users supply the text (or the pattern) and your fingerprint uses a hard-coded base and modulus that anyone can read. Constructing windows that share the pattern's fingerprint is then straightforward algebra, not luck. An attacker submits a document where **every** offset is such a window. Each one triggers a confirmation, each confirmation reads up to m symbols and fails, and the scan does (n-m+1)*m work. For a 1 MB document and a 1 KB pattern that is a billion symbol comparisons per request — a single cheap request consuming enormous CPU, repeated at will. This is a request-amplification denial of service, and it is the reason "expected O(n+m)" should never be written on a design document as though it were a guarantee. ## Route two: honest, unavoidable matches The second route has no attacker. If the pattern occurs at a constant fraction of offsets — a repetitive fixed-width record format, a padded field, a low-complexity sequence — then confirmations fire because they should. Even a hypothetically collision-free fingerprint cannot help: the matches are real and each is confirmed at O(m). This case is *output-driven*, and recognising it is what separates a candidate who has thought about the algorithm from one reciting a bound. The fix is not a better hash; it is either accepting the cost, capping reported occurrences, or choosing a matcher that reports overlapping occurrences without re-reading each one. ## Defending the first route **Randomize the parameters per run and keep them internal.** Draw the base (and where practical the modulus) from a wide family at process start. An attacker who cannot observe them cannot precompute collisions, which restores the coin flips the expected bound needs. Do not derive them from anything guessable, and do not report them in errors or debug output. **Widen the range.** The accidental hit rate is about n/M; going from a narrow to a wide fixed-width modulus takes that from "visible in the profile" to "never happens". Width is the cheapest lever available and it costs nothing per operation. **Bound the damage.** Count confirmations per scan. If they exceed a small multiple of the expected count, you are either under attack or on pathological input; switch to a matcher with a worst-case linear guarantee, or reject the request. Cap the text and pattern sizes a single request may present. Put the scan on a timeout and a CPU budget like any other untrusted-input workload. **Measure it.** Confirmations-per-scan is a genuinely useful metric: it is near zero in health, and it is the first number to move under both attack and input drift. A p99 latency spike on a search path with a flat request rate and a climbing confirmation count is a diagnosis, not a mystery. ## The judgment to voice Rabin-Karp's expected bound is excellent and its worst case is bad, and which one you get is decided by who controls the input. On your own trusted corpus with sensible parameters, the expected bound is a fair description of production behaviour. On attacker-supplied input with fixed public parameters, the worst case is not a tail risk — it is a target. State that distinction explicitly; it is the answer the question is really asking for.
- Does randomizing the base per run change the worst-case bound?No. The worst case stays O(n*m); randomization only makes it unreachable by an attacker who cannot see the parameters, because they can no longer construct the colliding input on purpose. It converts a targeted certainty into a probability over your own coin flips. If you need a genuine bound rather than a probabilistic defence, you need a matcher with a worst-case linear guarantee, or a hard cap and a bail-out.
- How would you distinguish a collision attack from ordinary repetitive input in production?Both drive confirmations up, so the counter alone will not separate them. Look at the correlation: pathological corpora arrive from your own ingestion path and are stable over time, while an attack shows a narrow set of clients, near-identical request shapes, and confirmations that fall to zero for the same volume from everyone else. Sampling a few offending documents settles it in a minute.
- Why can't a better hash fix the case where the pattern occurs at most offsets?Because those confirmations are not spurious — the occurrences are real and something must inspect them. A perfect, collision-free fingerprint would still hit at every one of those offsets. That branch of the worst case is output-driven, so the levers are different: cap the number of reported occurrences, or use a matcher that reports overlapping occurrences without re-reading each window.
saying these in an interview costs you the question
- Treats expected O(n+m) as a guaranteed bound
- Says the worst case cannot happen in practice
- Hard-codes a public base and modulus for untrusted input
- Confuses expected-case with amortized or worst-case bounds
- Believes a wider modulus removes the adversarial worst case