Rabin Karp String Matching in DSA C - Time & Space Complexity
We want to understand how the time taken by Rabin Karp string matching changes as the text and pattern sizes grow.
Specifically, how many steps does it take to find a pattern inside a text?
Analyze the time complexity of the following Rabin Karp implementation.
int rabinKarp(char *text, char *pattern) {
int n = strlen(text);
int m = strlen(pattern);
int pHash = 0, tHash = 0, h = 1;
int d = 256, q = 101; // base and prime
for (int i = 0; i < m - 1; i++)
h = (h * d) % q;
for (int i = 0; i < m; i++) {
pHash = (d * pHash + pattern[i]) % q;
tHash = (d * tHash + text[i]) % q;
}
for (int i = 0; i <= n - m; i++) {
if (pHash == tHash) {
int j;
for (j = 0; j < m; j++) {
if (text[i + j] != pattern[j])
break;
}
if (j == m)
return i; // match found
}
if (i < n - m) {
tHash = (d * (tHash - text[i] * h) + text[i + m]) % q;
if (tHash < 0) tHash += q;
}
}
return -1; // no match
}
This code searches for a pattern inside a text using hashing to speed up comparisons.
Look at the loops and repeated steps:
- Primary operation: The main loop slides over the text from start to end, comparing hashes and sometimes characters.
- How many times: The main loop runs about (n - m + 1) times, where n is text length and m is pattern length.
- Inside the main loop, 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 |
| 100 | About 100 hash checks, few character checks |
| 1000 | About 1000 hash checks, few character checks |
Pattern length m affects character checks only when hashes match, which is rare with a good hash.
So, the total steps grow roughly in a straight line as text size grows.
Time Complexity: O(n + m)
This means the time grows mostly in a straight line with the size of the text plus the pattern.
[X] Wrong: "Rabin Karp always checks every character in the text against the pattern."
[OK] Correct: Actually, it uses hash values to skip many character comparisons, so it usually runs faster than checking every character.
Understanding Rabin Karp's time complexity shows you how hashing helps speed up searching, a useful skill for many coding problems.
"What if we used a larger prime number for hashing? How would the time complexity change?"
Practice
Rabin Karp String Matching algorithm?Solution
Step 1: Understand the algorithm's approach
Rabin Karp uses hashing to convert strings into numbers for quick comparison.Step 2: Compare hash values instead of characters
It compares hash values of the pattern and text substrings to find matches efficiently.Final Answer:
Use hashing to find pattern matches efficiently -> Option BQuick Check:
Hashing = Efficient matching [OK]
- Thinking it compares characters directly always
- Confusing with brute force matching
- Assuming sorting is involved
s[i..i+m-1] to s[i+1..i+m]?Solution
Step 1: Understand rolling hash update formula
Remove the leftmost character's weighted value, multiply by base, add new character.Step 2: Apply formula to substring shift
hash = (hash - s[i] * power) * base + s[i+m] correctly updates the hash.Final Answer:
hash = (hash - s[i] * power) * base + s[i+m] -> Option CQuick Check:
Remove left char, multiply, add right char [OK]
- Adding instead of subtracting old char
- Wrong order of operations
- Mixing indices of characters
#include
#include
#define base 256
#define prime 101
int main() {
char text[] = "ababc";
char pattern[] = "abc";
int n = strlen(text), m = strlen(pattern);
int p = 0, t = 0, h = 1, i, j;
for (i = 0; i < m-1; i++)
h = (h * base) % prime;
for (i = 0; i < m; i++) {
p = (base * p + pattern[i]) % prime;
t = (base * t + text[i]) % prime;
}
for (i = 0; i <= n - m; i++) {
if (p == t) {
for (j = 0; j < m; j++) {
if (text[i+j] != pattern[j])
break;
}
if (j == m)
printf("Pattern found at index %d\n", i);
}
if (i < n - m) {
t = (base * (t - text[i] * h) + text[i + m]) % prime;
if (t < 0) t = t + prime;
}
}
return 0;
}Solution
Step 1: Calculate initial hashes and check first substring
Pattern "abc" hash matches substring "aba" at index 0? No, characters differ.Step 2: Slide window and check next substring
At index 1, substring "bab" hash differs. At index 2, substring "abc" matches pattern exactly.Final Answer:
Pattern found at index 2 -> Option AQuick Check:
Match at index 2 = Pattern found at index 2 [OK]
- Assuming match at index 0 without verification
- Ignoring negative hash correction
- Not verifying characters after hash match
t = (base * (t - text[i] * h) + text[i + m]) % prime; if (t < 0) t = t - prime;
Solution
Step 1: Understand negative hash correction
If hash becomes negative after modulo, add prime to keep it positive.Step 2: Check given code's correction
Code subtracts prime when t < 0, which makes t more negative. It should add prime.Final Answer:
Should add prime instead of subtracting when t < 0 -> Option AQuick Check:
Negative hash fix = add prime [OK]
- Subtracting prime instead of adding
- Ignoring negative hash values
- Confusing modulo operation behavior
Solution
Step 1: Count possible substrings of length 3 in text
Text length 6, pattern length 3, so 6 - 3 + 1 = 4 substrings.Step 2: Check each substring for pattern match
All substrings "aaa" at indices 0,1,2,3 match pattern including overlaps.Final Answer:
4 matches, because overlapping occurrences are counted -> Option DQuick Check:
Overlapping matches counted = 4 [OK]
- Ignoring overlapping matches
- Assuming only non-overlapping matches count
- Confusing hash collisions with matches
Ready to prove it?
Pass the test and this pattern moves unknown
