String Algorithms
String Problems in CP
String algorithms unlock pattern matching, palindrome detection, and text processing problems. The key is choosing the right tool. Why specialized algorithms? Naive string matching (check every position) is O(n*m). For n = m = 10^6, that is 10^12 operations—far too slow. KMP, Z-function, and hashing each bring this down to O(n + m) by cleverly reusing work from previous comparisons. The intuition behind all of them: when a mismatch occurs, you have already matched some characters, and that partial match tells you where to look next without starting over.Pattern Recognition Signals:
- “Find pattern in text” → KMP, Z-function, or Hashing
- “Count occurrences of pattern” → KMP or Hashing
- “Longest palindrome” → Manacher’s algorithm
- “Prefix queries on dictionary” → Trie
- “Compare substrings” → Hashing with binary search
Algorithm Selection Guide
String Hashing
Convert strings to numbers for fast comparison. The Problem: Comparing two strings of length n takes O(n). For many comparisons, this is too slow. The Insight: Map each string to a number (hash). If hashes differ, strings differ. If hashes match, strings are probably equal. How it works: Treat the string as a number in base B: Example: “abc” with B=31: Substring Hash in O(1): Using prefix hashes: Collision Risk: Two different strings can have the same hash. Use:- Large prime MOD (10^9 + 7)
- Double hashing: Two different (BASE, MOD) pairs—collision probability becomes ~1/10^18
Double Hashing (Reduces Collision)
KMP Algorithm
Find all occurrences of pattern in text in O(n + m). The Problem: Naive search is O(n·m)—for each position in text, compare entire pattern. KMP’s Insight: When a mismatch occurs, we’ve already matched some characters. Use this information to skip ahead instead of starting over.Computing Failure Function (LPS Array)
LPS[i] = length of the Longest Proper Prefix of pattern[0..i] which is also a Suffix. Example: pattern = “ABABAC”Pattern Matching
Z-Function
z[i] = length of longest substring starting from i that matches a prefix of s. Example: s = “aabxaab”- Create combined = P + ” is a separator not in P or T)
- Compute Z-function
- Wherever z[i] == len(P), we found a match at position i - len(P) - 1 in T
Trie (Prefix Tree)
Efficient for prefix-based queries on a dictionary. Structure: A tree where:- Each edge is labeled with a character
- Each node represents a prefix (path from root)
- Words sharing a prefix share that path
- Insert/Search: O(length of word)
- Count words with prefix: O(length of prefix)
- Autocomplete: O(prefix length + number of suggestions)
- Dictionary with prefix queries
- XOR maximization (binary trie)
- Counting distinct prefixes
Pattern 1: Longest Palindromic Substring
Using Hashing + Binary Search
Manacher’s Algorithm (O(n))
The Problem: Find all palindromes in O(n) time. The Trick: Insert ’#’ between characters to handle even-length palindromes uniformly.- “abba” becomes “#a#b#b#a#”
- Now all palindromes have odd length with a center
Pattern 2: Distinct Substrings
Problem: Count number of distinct substrings.Pattern 3: Longest Common Substring
Pattern 4: Aho-Corasick (Multiple Pattern Matching)
For searching multiple patterns simultaneously.Common Mistakes
Practice Problems
Beginner (1000-1300)
Intermediate (1300-1600)
Advanced (1600-1900)
Key Takeaways
Hashing
O(1) substring comparison after O(n) preprocessing.
KMP/Z-function
O(n + m) pattern matching without false positives.
Trie
Efficient prefix queries and autocomplete.
Manacher
O(n) for all palindromic substrings.
Next Up
Chapter 19: Bit Manipulation
Master bitwise operations, bitmasks, and XOR tricks.