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 in a text?
Analyze the time complexity of the following Rabin Karp code snippet.
public bool RabinKarp(string text, string pattern) {
int n = text.Length, m = pattern.Length;
if (m == 0) return true;
if (m > n) return false;
int prime = 101, baseVal = 256;
long patternHash = 0, textHash = 0, h = 1;
for (int i = 0; i < m - 1; i++)
h = (h * baseVal) % prime;
for (int i = 0; i < m; i++) {
patternHash = (baseVal * patternHash + pattern[i]) % prime;
textHash = (baseVal * textHash + text[i]) % prime;
}
for (int i = 0; i < n - m + 1; i++) {
if (patternHash == textHash) {
// Check characters one by one
int j;
for (j = 0; j < m; j++) {
if (text[i + j] != pattern[j]) break;
}
if (j == m) return true;
}
if (i < n - m) {
textHash = (baseVal * (textHash - text[i] * h) + text[i + m]) % prime;
if (textHash < 0) textHash += prime;
}
}
return false;
}
This code searches for a pattern in a text using hashing to speed up comparisons.
Identify the loops and repeated checks.
- Primary operation: Sliding over the text with a loop that runs (n - m + 1) times.
- How many times: For each position, it compares hashes and sometimes characters one by one (up to m times).
As the text length (n) grows, the main loop runs roughly n times. The pattern length (m) affects how many characters we check when hashes match.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 hash checks, few character checks |
| 100 | About 100 hash checks, some character checks |
| 1000 | About 1000 hash checks, some character checks |
Pattern length m affects character checks only when hashes match, which is rare if hash is good.
So, execution grows mostly linearly with text size.
Time Complexity: O(n + m)
This means the algorithm checks the text and pattern mostly once each, making it efficient for large inputs.
[X] Wrong: "Rabin Karp always compares every character in the text to the pattern."
[OK] Correct: It uses hashing to skip many character comparisons, only checking characters when hashes match, which is rare.
Understanding Rabin Karp's time complexity shows you can analyze 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 that affect the time complexity?"
Practice
Rabin Karp String Matching algorithm?Solution
Step 1: Understand Rabin Karp approach
Rabin Karp uses hashing to convert strings into numbers for quick comparison.Step 2: Compare hash values instead of characters
It compares hash of pattern with hash of text substrings to find matches efficiently.Final Answer:
Use hashing to find pattern matches efficiently -> Option CQuick Check:
Hashing = Efficient matching [OK]
- Thinking it compares characters one by one always
- Confusing with brute force matching
- Assuming sorting is involved
Solution
Step 1: Understand rolling hash formula
The rolling hash updates as hash = (hash * base + new_char) % prime.Step 2: Apply formula for initial hash
Initial hash is calculated by iterating characters and applying this formula cumulatively.Final Answer:
hash = (hash * base + char) % prime -> Option DQuick Check:
Rolling hash formula = hash = (hash * base + char) % prime [OK]
- Adding instead of multiplying by base
- Subtracting characters incorrectly
- Ignoring modulo operation
string text = "ababc";
string pattern = "abc";
List<int> matches = RabinKarpSearch(text, pattern);
Console.WriteLine(string.Join(",", matches));
List<int> RabinKarpSearch(string txt, string pat) {
int baseVal = 256, prime = 101;
int m = pat.Length, n = txt.Length;
int patHash = 0, txtHash = 0, h = 1;
List<int> result = new List<int>();
for (int i = 0; i < m - 1; i++) h = (h * baseVal) % prime;
for (int i = 0; i < m; i++) {
patHash = (baseVal * patHash + pat[i]) % prime;
txtHash = (baseVal * txtHash + txt[i]) % prime;
}
for (int i = 0; i <= n - m; i++) {
if (patHash == txtHash) {
bool match = true;
for (int j = 0; j < m; j++) {
if (txt[i + j] != pat[j]) {
match = false;
break;
}
}
if (match) result.Add(i);
}
if (i < n - m) {
txtHash = (baseVal * (txtHash - txt[i] * h) + txt[i + m]) % prime;
if (txtHash < 0) txtHash += prime;
}
}
return result;
}Solution
Step 1: Identify pattern occurrences in text
Pattern "abc" appears starting at index 2 in "ababc". Also, substring at index 0 is "aba" which is not a match.Step 2: Check hash and character matches
Hashes match only at index 2 (initial hashes differ at index 0 and 1), and character check passes at index 2. So only index 2 is added.Final Answer:
2 -> Option BQuick Check:
Pattern found at index 2 [OK]
- Assuming all hash matches are valid
- Ignoring character verification after hash match
- Miscounting indices starting from 1
int baseVal = 256, prime = 101;
int m = pattern.Length, n = text.Length;
int patHash = 0, txtHash = 0, h = 1;
for (int i = 0; i <= m - 1; i++) h = (h * baseVal) % prime;
for (int i = 0; i < m; i++) {
patHash = (baseVal * patHash + pattern[i]) % prime;
txtHash = (baseVal * txtHash + text[i]) % prime;
}
// rest of code omittedSolution
Step 1: Analyze h calculation loop
h is used as base^(m-1) % prime, so loop should run m-1 times, i.e., i from 0 to m-2.Step 2: Identify off-by-one error
Loop runs from i=0 to i=m-1 (inclusive), which is m times, causing incorrect h value.Final Answer:
Loop for h calculation should run till m-2, not m-1 -> Option AQuick Check:
h loop runs m-1 times [OK]
- Using <= instead of < in loop
- Forgetting modulo in hash updates
- Wrong base value choice
Solution
Step 1: Understand hash collisions in Rabin Karp
Hash collisions occur when different substrings produce same hash value, causing false positives.Step 2: Use larger prime to reduce collisions
A larger prime number reduces chances of collisions by spreading hash values more evenly.Final Answer:
Use a larger prime number for modulo operations -> Option AQuick Check:
Larger prime = fewer collisions [OK]
- Skipping character checks after hash match
- Using base 1 which weakens hash uniqueness
- Checking only first character after hash match
Ready to prove it?
Pass the test and this pattern moves unknown
