Rabin Karp String Matching in DSA C++ - 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 inside a larger text?
Analyze the time complexity of the following Rabin Karp code snippet.
int rabinKarp(string text, string pattern) {
int n = text.size();
int m = pattern.size();
int pHash = 0, tHash = 0, base = 256, mod = 101;
int h = 1;
for (int i = 0; i < m - 1; i++)
h = (h * base) % mod;
for (int i = 0; i < m; i++) {
pHash = (base * pHash + pattern[i]) % mod;
tHash = (base * tHash + text[i]) % mod;
}
for (int i = 0; i <= n - m; i++) {
if (pHash == tHash) {
bool match = true;
for (int j = 0; j < m; j++) {
if (text[i + j] != pattern[j]) {
match = false;
break;
}
}
if (match) return i;
}
if (i < n - m) {
tHash = (base * (tHash - text[i] * h) + text[i + m]) % mod;
if (tHash < 0) tHash += mod;
}
}
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: Sliding the pattern over the text and comparing hashes.
- How many times: The main loop runs about (n - m + 1) times, where n is text length and m is pattern length.
- Inside that, if hashes match, a character-by-character check runs up to m times.
As the text size grows, the number of hash comparisons grows roughly linearly.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 hash checks, few character checks if collisions occur |
| 100 | About 100 hash checks, some character checks on hash matches |
| 1000 | About 1000 hash checks, occasional character checks |
Pattern size m affects character checks only when hashes match, which is rare with a good hash.
Time Complexity: O(n + m)
This means the algorithm usually checks each character in the text and pattern 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 character comparisons, only checking characters when hashes match.
Understanding Rabin Karp's time complexity shows you how hashing can speed up searching tasks, a useful skill in many coding problems.
"What if the hash function causes many collisions? How would the time complexity change?"
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 text substrings 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 BQuick Check:
Hashing = Efficient matching [OK]
- Thinking it compares characters directly always
- Assuming it sorts strings before matching
- Confusing it with recursive search methods
s[i..i+m-1] to s[i+1..i+m]?Solution
Step 1: Understand rolling hash update
Remove the leftmost character's contribution, multiply remaining hash by base, then add new character's value.Step 2: Apply formula for next substring
This keeps hash calculation efficient without recomputing from scratch.Final Answer:
Remove leftmost char's hash, multiply by base, add new char's hash -> Option CQuick Check:
Rolling hash update = Remove + multiply + add [OK]
- Recalculating hash fully each time
- Adding instead of removing old char's hash
- Multiplying after adding new char instead of before
std::string text = "ababc"; std::string pattern = "abc"; // Assume correct Rabin Karp implementation std::vector<int> matches = rabinKarpSearch(text, pattern); for (int pos : matches) std::cout << pos << " ";
Solution
Step 1: Identify pattern occurrences in text
Pattern "abc" appears starting at index 2 in "ababc" (0-based indexing).Step 2: Confirm no other matches
Substring at index 0 is "aba", not "abc"; index 1 is "bab", not "abc".Final Answer:
2 -> Option DQuick Check:
Pattern found at index 2 [OK]
- Assuming match at index 0 incorrectly
- Confusing overlapping substrings
- Using 1-based indexing instead of 0-based
hash = (hash - text[i] * pow(base, m-1)) * base + text[i+m];What is the likely problem?
Solution
Step 1: Check arithmetic operations in rolling hash
The formula subtracts old char's hash, multiplies by base, adds new char's value.Step 2: Identify missing modulo usage
Without modulo, hash values can overflow integer limits causing wrong results.Final Answer:
Missing modulo operation causing overflow -> Option AQuick Check:
Modulo needed to prevent overflow [OK]
- Ignoring modulo leading to wrong hash values
- Using wrong power exponent
- Mixing addition and subtraction signs
Solution
Step 1: Understand multi-pattern matching challenge
Running Rabin Karp separately for each pattern is inefficient for many patterns.Step 2: Use combined hashing approach
Hash all patterns and store in a set. Then slide over text, hashing substrings and checking set membership.Step 3: Efficiently find all matches
This reduces repeated work and speeds up multi-pattern search.Final Answer:
Use a combined hash set of all patterns and check text substrings against it -> Option AQuick Check:
Combined hashing = efficient multi-pattern search [OK]
- Running separate searches for each pattern
- Sorting patterns instead of hashing
- Using recursion unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
