String Algorithms — KMP, Rabin-Karp, and Pattern Matching
669 words
3 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# String Algorithms — KMP, Rabin-Karp, and Pattern Matching ## 🎯 Learning Objectives - Implement the KMP prefix function - Trace KMP string matching with prefix function - Apply Rabin-Karp rolling hash - Compare string matching algorithm tradeoffs * * * ## 1. Naive String Matching **Algorithm:** Slide pattern over...

String Algorithms — KMP, Rabin-Karp, and Pattern Matching
🎯 Learning Objectives
- Implement the KMP prefix function
- Trace KMP string matching with prefix function
- Apply Rabin-Karp rolling hash
- Compare string matching algorithm tradeoffs
1. Naive String Matching
Algorithm: Slide pattern over text, compare at each position.
pythondef naive_search(text, pattern): n, m = len(text), len(pattern) matches = [] for i in range(n - m + 1): for j in range(m): if text[i + j] != pattern[j]: break else: # all matched matches.append(i) return matches
Time: O(nm) worst-case (e.g., text = "AAAAAA", pattern = "AAA").
2. Knuth-Morris-Pratt (KMP)
2.1 Prefix Function
π[q] = length of longest proper prefix of P[0..q] that is also a suffix of P[0..q].
Tracing: P = "ABABACA"
| i | P[i] | π[i] | Explanation |
|---|---|---|---|
| 0 | A | 0 | No proper prefix |
| 1 | B | 0 | "A" ≠ "B" |
| 2 | A | 1 | "A" = "A" (prefix of length 1 matches suffix) |
| 3 | B | 2 | "AB" = "AB" |
| 4 | A | 3 | "ABA" = "ABA" |
| 5 | C | 0 | "ABAB" ≠ "BAC" |
| 6 | A | 1 | "A" = "A" |
2.2 KMP Search Tracing
Text: "ABABACABA", Pattern: "ABABACA" (m=7)
| Step | Text[i] | Pattern[j] | Match? | Shift (π[j-1]) |
|---|---|---|---|---|
| 0-6 | ABABAC | ABABAC | Yes, j=6 | — |
| 7 | A | A | Yes, j=7 | Match found at 0! |
| 1 | B | B (π[6]=1, try P[1]=B) | Yes, j=2 | Continue |
| 2 | A | A | Yes, j=3 | Continue |
| 3 | B | B | Yes, j=4 | Continue |
| 4 | A | A | Yes, j=5 | Continue |
| 5 | C | C | Yes, j=6 | Continue |
| 6 | A | A | Yes, j=7 | Match at 2! |
Wait, let me redo this more carefully. After match at position 0, we set j=π[6]=1, so we start matching pattern position 1 against text position 7.
Text: A B A B A C A B A i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7 i=8 Pat: A B A B A C A j=0 j=1 j=2 j=3 j=4 j=5 j=6
Match at 0: i=7, j=7 → match! Set j=π[6]=1. Text[7]=B, P[1]=B → match. j=2, Text[8]=A, P[2]=A → match. j=3. End of text. Match at position 2.
Time: O(n+m) — linear!
3. Rabin-Karp Algorithm
3.1 Rolling Hash
Hash the pattern. For each window of text, compute hash in O(1) using previous hash.
Hash function: h=(dm−1⋅T[i]+...+d0⋅T[i+m−1])modq
Rolling update: hnew=(d⋅(hold−T[i]⋅dm−1)+T[i+m])modq
3.2 Tracing
Text: "ABCDEFG", Pattern: "CDE", d=256, q=101
| Window | Hash | Match? |
|---|---|---|
| ABC | (256²×65+256×66+67) mod 101 = h₁ | No |
| BCD | (256×(h₁-65×256²)+68) mod 101 = h₂ | No |
| CDE | (256×(h₂-66×256²)+69) mod 101 = h₃ | Yes! Verify: C=C, D=D, E=E ✓ |
| DEF | ... | No |
4. Algorithm Comparison
| Algorithm | Preprocessing | Matching | Space | Use Case |
|---|---|---|---|---|
| Naive | None | O(nm) | O(1) | Short patterns, random text |
| KMP | O(m) | O(n+m) | O(m) | Any, worst-case stable |
| Rabin-Karp | O(m) | O(n+m) avg, O(nm) worst | O(1) | Multiple patterns |
| Boyer-Moore | O(m+σ) | O(n/m) best, O(nm) worst | O(m+σ) | Long patterns, large alphabets |
| Z-algorithm | O(n+m) | O(n+m) | O(n+m) | Combined string analysis |
5. Common Pitfalls
Pitfall: Hash Collisions in Rabin-Karp
The mistake: Hash matches but strings are different (collision) → false positive.
Correct approach: Always verify character-by-character when hash matches. Good hash function (large q) reduces collisions but doesn't eliminate them.
6. Key Concepts Reference
| Concept | Definition | Complexity |
|---|---|---|
| Prefix function | π[i] = longest proper prefix = suffix | O(m) |
| Rolling hash | O(1) update between windows | O(1) per position |
| KMP shift | j = π[j-1] on mismatch | Tracks progress |
| Modular hash | Hash mod q to avoid overflow | O(m) initial |
7. 📝 Practice Questions
Q1: Compute prefix function for "AABAACA".Answer: P = A A B A A C A π = [0, 1, 0, 1, 2, 0, 1]Check: i=4, P[0..4]="AABAA", proper prefix "AA" = suffix "AA" → π[4]=2. i=6, P[0..6]="AABAACA", "A" = "A" → π[6]=1. Q2: KMP processes text of length 1000 with pattern length 100. Maximum comparisons?Answer: O(n+m) = O(1000+100) = 1100 comparisons max. Naive would do up to O(1000×100) = 100,000. KMP guarantees linear time regardless of input. Q3: Rabin-Karp: text="CCCCCC", pattern="CCC", d=256, q=7. Trace.Answer: Hash pattern "CCC": (256²×67+256×67+67) mod 7. 256 mod 7 = 4. 4²×67=16×67=1072 mod 7 = 1. 4×67=268 mod 7 = 2. 67 mod 7 = 4. Total = 1+2+4=7 mod 7 = 0. First window "CCC": also hash 0. Verify: all C's match → position 0 match. Each subsequent window also hashes to 0 → positions 1,2,3 also match. Collisions possible with different strings that also hash to 0.
8. 🔗 Cross-References
- Week 3 - Dynamic Programming: Sequence alignment
- Week 6 - NP-Completeness: String problems
- BSCS4020 (DSA): Basic string operations Join Discord PreviousFlow ApplicationsNextNP-Completeness