Rabin-Karp Algorithm
Uses rolling polynomial hashing to find pattern occurrences in O(n + m) average time; especially useful for multi-pattern search and plagiarism detection.
Syntax
rabinKarp(text, pattern) Parameters
| Name | Type | Required | Description |
|---|---|---|---|
text | string | Yes | The string to search within (length n). |
pattern | string | Yes | The pattern to search for (length m). |
Returns
number[] — Array of starting indices where pattern is found in text.
Examples
function rabinKarp(text, pattern) {
const BASE = 31, MOD = 1_000_000_007n;
const n = text.length, m = pattern.length;
if (m > n) return [];
const charCode = c => BigInt(c.charCodeAt(0) - 96);
let patHash = 0n, winHash = 0n, power = 1n;
for (let i = 0; i < m; i++) {
patHash = (patHash * BigInt(BASE) + charCode(pattern[i])) % MOD;
winHash = (winHash * BigInt(BASE) + charCode(text[i])) % MOD;
if (i > 0) power = (power * BigInt(BASE)) % MOD;
}
const matches = [];
for (let i = 0; i <= n - m; i++) {
if (winHash === patHash && text.slice(i, i + m) === pattern)
matches.push(i);
if (i < n - m) {
winHash = ((winHash - charCode(text[i]) * power) * BigInt(BASE) + charCode(text[i + m])) % MOD;
if (winHash < 0n) winHash += MOD;
}
}
return matches;
}
console.log(rabinKarp('abcaabcaa', 'abc'));
Output
[ 0, 4 ]
// Hash collision: always verify with direct comparison (already done above)
function rabinKarp(text, pattern) {
const BASE=31, MOD=1_000_000_007n, cc=c=>BigInt(c.charCodeAt(0)-96);
const n=text.length, m=pattern.length;
if(m>n) return [];
let ph=0n, wh=0n, pw=1n;
for(let i=0;i<m;i++){ph=(ph*BigInt(BASE)+cc(pattern[i]))%MOD;wh=(wh*BigInt(BASE)+cc(text[i]))%MOD;if(i>0)pw=(pw*BigInt(BASE))%MOD;}
const res=[];
for(let i=0;i<=n-m;i++){
if(wh===ph&&text.slice(i,i+m)===pattern)res.push(i);
if(i<n-m){wh=((wh-cc(text[i])*pw)*BigInt(BASE)+cc(text[i+m]))%MOD;if(wh<0n)wh+=MOD;}
}
return res;
}
// Pattern same as text
console.log(rabinKarp('aaaa', 'aa'));
Output
[ 0, 1, 2 ]
Notes
| Case | Time | Space |
|---------|-----------|-------|
| Best | O(n + m) | O(1) |
| Average | O(n + m) | O(1) |
| Worst | O(nm) | O(1) |
Worst case O(nm) occurs when every window is a hash collision (spurious
hit), requiring a full string comparison each time. A strong hash
(large prime modulus) makes this astronomically rare. The rolling hash
removes the leading character and adds the trailing one in O(1), making
each step O(1). Rabin-Karp is particularly attractive for **multiple
pattern search**: precompute all pattern hashes and use a hash set for
O(1) lookup per window.