Rabin Karp String Matching in DSA Java - 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 boolean rabinKarp(String text, String pattern) {
int n = text.length();
int m = pattern.length();
int patternHash = hash(pattern);
int textHash = hash(text.substring(0, m));
for (int i = 0; i <= n - m; i++) {
if (patternHash == textHash && text.substring(i, i + m).equals(pattern)) {
return true;
}
if (i < n - m) {
textHash = rollHash(textHash, text.charAt(i), text.charAt(i + m));
}
}
return false;
}
This code slides a window over the text, compares hash values, and checks for matches.
Look for loops and repeated checks.
- Primary operation: Loop sliding over the text from start to end minus pattern length.
- How many times: Approximately n - m + 1 times, where n is text length and m is pattern length.
As the text gets longer, the number of windows to check grows roughly linearly.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 checks |
| 100 | About 100 checks |
| 1000 | About 1000 checks |
Pattern length m is usually smaller, so the main growth depends on text length n.
Time Complexity: O(n + m) on average
This means the algorithm checks the text in a way that grows mostly in a straight line with the text and pattern sizes.
[X] Wrong: "Rabin Karp always runs in O(n) time no matter what."
[OK] Correct: In the worst case, many hash collisions cause repeated substring checks, making it slower, closer to O(n*m).
Understanding Rabin Karp's time complexity helps you explain how hashing speeds up searching and when it might slow down.
"What if we used a different hash function that never caused 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.Final Answer:
It uses hashing to find pattern matches efficiently -> Option CQuick Check:
Hashing = Efficient matching [OK]
- Thinking it compares characters directly without hashing
- Confusing it with brute force matching
- Assuming it sorts strings before matching
Solution
Step 1: Recall rolling hash formula
Rolling hash removes the old char's contribution and adds the new char's value.Step 2: Apply formula correctly
It subtracts old_char * high_base, multiplies by base, then adds new_char.Final Answer:
new_hash = (old_hash - old_char * high_base) * base + new_char -> Option AQuick Check:
Rolling hash formula = new_hash = (old_hash - old_char * high_base) * base + new_char [OK]
- Adding old_char instead of subtracting
- Dividing instead of multiplying by base
- Ignoring the high_base multiplier
String text = "ababc"; String pattern = "abc"; List<Integer> result = rabinKarpSearch(text, pattern); System.out.println(result);Assume
rabinKarpSearch returns a list of starting indices where pattern matches.Solution
Step 1: Identify pattern occurrences in text
Text: "ababc"; pattern: "abc"; check substrings of length 3: "aba"(0), "bab"(1), "abc"(2).Step 2: Find matching indices
Only substring starting at index 2 matches "abc".Final Answer:
[2] -> Option DQuick Check:
Pattern found at index 2 only [OK]
- Including partial matches like "aba" or "bab"
- Assuming multiple matches without checking substring
- Confusing indices starting at 1 instead of 0
int base = 256; int prime = 101; int highBase = (int) Math.pow(base, pattern.length()); // ... rest of code // rolling hash update: currentHash = (currentHash - text.charAt(i) * highBase) * base + text.charAt(i + pattern.length()); currentHash %= prime;What is the main bug here?
Solution
Step 1: Check highBase calculation
High base is used to remove the leftmost character's contribution, so it should be base^(pattern length - 1).Step 2: Identify incorrect power usage
Using base^(pattern length) removes too much, causing wrong hash updates.Final Answer:
highBase should be base^(pattern.length() - 1), not base^pattern.length() -> Option AQuick Check:
Correct highBase power fixes rolling hash [OK]
- Using base^pattern.length() instead of base^(pattern.length()-1)
- Ignoring modulo after subtraction
- Confusing addition and subtraction in rolling hash
Solution
Step 1: Identify all substrings of length 3
Text "aaaaaa" has substrings: "aaa"(0), "aaa"(1), "aaa"(2), "aaa"(3).Step 2: Count overlapping matches
All these substrings match pattern "aaa", so 4 matches found including overlaps.Final Answer:
4, because overlapping matches are counted -> Option BQuick Check:
Overlapping matches count = 4 [OK]
- Assuming matches cannot overlap
- Stopping search after first match
- Counting only non-overlapping matches
Ready to prove it?
Pass the test and this pattern moves unknown
