Rabin Karp String Matching in DSA Javascript - Time & Space Complexity
We want to understand how the Rabin Karp algorithm's running time changes as the text and pattern sizes grow.
Specifically, how many steps does it take to find a pattern in a text?
Analyze the time complexity of the following Rabin Karp code snippet.
function rabinKarp(text, pattern) {
const n = text.length;
const m = pattern.length;
const base = 256;
const prime = 101;
let patternHash = 0, textHash = 0, h = 1;
for (let i = 0; i < m - 1; i++) {
h = (h * base) % prime;
}
for (let i = 0; i < m; i++) {
patternHash = (base * patternHash + pattern.charCodeAt(i)) % prime;
textHash = (base * textHash + text.charCodeAt(i)) % prime;
}
for (let i = 0; i <= n - m; i++) {
if (patternHash === textHash) {
let match = true;
for (let j = 0; j < m; j++) {
if (text[i + j] !== pattern[j]) {
match = false;
break;
}
}
if (match) return i;
}
if (i < n - m) {
textHash = (base * (textHash - text.charCodeAt(i) * h) + text.charCodeAt(i + m)) % prime;
if (textHash < 0) textHash += prime;
}
}
return -1;
}
This code searches for a pattern inside a text using hashing to speed up comparisons.
Look at the loops and repeated checks:
- Primary operation: The outer loop sliding over the text (runs about n - m + 1 times).
- Secondary operation: The inner loop comparing characters when hashes match (runs up to m times, but only on hash matches).
As the text size (n) grows, the outer loop runs roughly n times. The pattern size (m) affects the inner character checks.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 hash checks, few character comparisons |
| 100 | About 100 hash checks, some character comparisons on matches |
| 1000 | About 1000 hash checks, character comparisons only on rare hash matches |
Pattern matching checks happen only when hashes match, so most of the time the algorithm quickly skips ahead.
Time Complexity: O(n + m) on average, O(nm) in the worst case due to hash collisions.
This means the algorithm usually checks each character in the text and pattern just once, making it efficient for large inputs.
[X] Wrong: "Rabin Karp always compares every character in the text to the pattern."
[OK] Correct: Actually, it uses hash values to skip many unnecessary character comparisons, so it rarely checks every character directly.
Understanding Rabin Karp's time complexity shows you how hashing helps speed up searching tasks, a useful skill for many coding problems.
"What if the hash function caused many collisions? How would that affect the time complexity?"
Practice
Rabin Karp String Matching algorithm?Solution
Step 1: Understand Rabin Karp's approach
The algorithm uses a hash function to convert the pattern and substrings of the text into numbers.Step 2: Use hashing for efficient matching
By comparing hash values instead of characters, it quickly finds potential matches and then verifies them.Final Answer:
Use hashing to find pattern matches efficiently -> Option CQuick Check:
Rabin Karp = hashing for matching [OK]
- Thinking it uses recursion
- Assuming it sorts strings before matching
- Confusing it with brute force method
Solution
Step 1: Understand hash calculation formula
The rolling hash is computed by multiplying the current hash by base and adding the new character code, then taking modulo prime.Step 2: Match formula with options
hash = (hash * base + charCode) % prime matches the formula: hash = (hash * base + charCode) % prime.Final Answer:
hash = (hash * base + charCode) % prime -> Option BQuick Check:
Rolling hash formula = hash = (hash * base + charCode) % prime [OK]
- Adding base multiplied by charCode instead of multiplying hash
- Mixing up modulo operations
- Using wrong order of operations
const text = 'ababc';
const pattern = 'abc';
// Assume base=256, prime=101
// Simplified hash function and matching logic
function rabinKarp(text, pattern) {
const m = pattern.length;
const n = text.length;
for (let i = 0; i <= n - m; i++) {
if (text.substring(i, i + m) === pattern) {
return i;
}
}
return -1;
}
console.log(rabinKarp(text, pattern));Solution
Step 1: Check substring matches
Check substrings of length 3 in 'ababc': 'aba'(index 0), 'bab'(1), 'abc'(2).Step 2: Identify matching index
'abc' matches at index 2, so function returns 2.Final Answer:
2 -> Option AQuick Check:
Pattern found at index 2 [OK]
- Returning first partial match index
- Off-by-one errors in loop
- Confusing substring indices
function rabinKarp(text, pattern) {
const base = 256;
const prime = 101;
let patternHash = 0, textHash = 0;
const m = pattern.length;
const n = text.length;
let h = 1;
for (let i = 0; i < m; i++) {
h = (h * base) % prime;
}
for (let i = 0; i < m; i++) {
patternHash = (base * patternHash + pattern.charCodeAt(i)) % prime;
textHash = (base * textHash + text.charCodeAt(i)) % prime;
}
for (let i = 0; i <= n - m; i++) {
if (patternHash === textHash) {
if (text.substring(i, i + m) === pattern) {
return i;
}
}
if (i < n - m) {
textHash = (base * (textHash - text.charCodeAt(i) * h) + text.charCodeAt(i + m)) % prime;
if (textHash < 0) textHash += prime;
}
}
return -1;
}Solution
Step 1: Examine the h calculation
The variable h should be base^(m-1) % prime to multiply the outgoing character's contribution.Step 2: Identify the loop error
The loop runs from i=0 to iFinal Answer:
Incorrect calculation of h in the first loop -> Option DQuick Check:
h = base^(m-1) % prime requires loop m-1 times [OK]
- Forgetting to add prime if textHash becomes negative
- Incorrect loop range for h calculation
- Not verifying substring match after hash match
Solution
Step 1: Understand multiple pattern matching challenge
Checking each pattern separately is slow for many patterns.Step 2: Use hashing for all patterns
Precompute hashes of all patterns and store in a hash set for O(1) lookup during text scanning.Step 3: Efficiently find all matches
When scanning text substrings, compute hash and check if it exists in the pattern hash set to find matches quickly.Final Answer:
Precompute hashes of all patterns and use a hash set for quick lookup -> Option AQuick Check:
Multiple patterns = precompute hashes + hash set [OK]
- Checking each pattern separately causing slow performance
- Sorting patterns instead of hashing
- Using recursion unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
