String Matching Patterns: Complete Reference¶
API Kernel:
StringMatchingCore Mechanism: Efficient substring search using preprocessing (KMP failure function, rolling hash) to avoid redundant character comparisons.
This document presents the canonical String Matching templates covering KMP algorithm and Rabin-Karp rolling hash. These techniques are fundamental for pattern matching problems where brute-force O(mn) is too slow.
Table of Contents¶
- Core Concepts
- Base Template: Find the Index of First Occurrence (LeetCode 28)
- Variant: Shortest Palindrome (LeetCode 214)
- Variant: Repeated Substring Pattern (LeetCode 459)
- Variant: Longest Happy Prefix (LeetCode 1392)
- Pattern Comparison Table
- Decision Tree
- Pattern Selection Guide
- Problem Type Recognition
- Universal Templates
- Quick Reference
1. Core Concepts¶
1.1 The String Matching Problem¶
Given a text t of length n and a pattern p of length m, find all occurrences of p in t.
Naive approach: O(n * m) - slide pattern and compare each position
KMP approach: O(n + m) - preprocess pattern to skip redundant comparisons
Rabin-Karp: O(n + m) average - use rolling hash for fast comparison
1.2 KMP: The Failure Function¶
The key insight of KMP is the failure function (also called prefix function or LPS array):
failure[i] = length of longest proper prefix of p[0:i+1] that is also a suffix
Example: pattern = "ABABAC"
Index: 0 1 2 3 4 5
Char: A B A B A C
LPS: 0 0 1 2 3 0
Explanation:
- failure[0] = 0: "A" has no proper prefix
- failure[1] = 0: "AB" has no matching prefix/suffix
- failure[2] = 1: "ABA" -> "A" is both prefix and suffix
- failure[3] = 2: "ABAB" -> "AB" is both prefix and suffix
- failure[4] = 3: "ABABA" -> "ABA" is both prefix and suffix
- failure[5] = 0: "ABABAC" -> no matching prefix/suffix
1.3 KMP Search Algorithm¶
def kmp_search(text: str, pattern: str) -> list[int]:
"""
Find all occurrences of pattern in text using KMP algorithm.
Key insight: When mismatch occurs at position j in pattern,
we don't restart from 0. Instead, we jump to failure[j-1]
because that prefix has already been matched.
Time: O(n + m) where n = len(text), m = len(pattern)
Space: O(m) for the failure function
"""
if not pattern:
return [0]
if not text or len(pattern) > len(text):
return []
# Build failure function
pattern_length = len(pattern)
failure = [0] * pattern_length
# Compute failure function using self-matching
prefix_length = 0
for i in range(1, pattern_length):
# Backtrack until we find a match or reach 0
while prefix_length > 0 and pattern[i] != pattern[prefix_length]:
prefix_length = failure[prefix_length - 1]
if pattern[i] == pattern[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
# Search for pattern in text
matches = []
pattern_idx = 0
for text_idx, text_char in enumerate(text):
# Backtrack pattern pointer on mismatch
while pattern_idx > 0 and text_char != pattern[pattern_idx]:
pattern_idx = failure[pattern_idx - 1]
if text_char == pattern[pattern_idx]:
pattern_idx += 1
if pattern_idx == pattern_length:
# Found a match
matches.append(text_idx - pattern_length + 1)
# Continue searching (overlapping matches)
pattern_idx = failure[pattern_idx - 1]
return matches
1.4 Rabin-Karp: Rolling Hash¶
Rabin-Karp uses a hash function to quickly compare pattern with text windows:
def rabin_karp_search(text: str, pattern: str) -> list[int]:
"""
Find all occurrences of pattern in text using Rabin-Karp.
Key insight: Use rolling hash to compute hash of each window in O(1).
hash(s[i:i+m]) can be computed from hash(s[i-1:i-1+m]) by:
1. Remove contribution of s[i-1]
2. Add contribution of s[i+m-1]
Time: O(n + m) average, O(nm) worst case (hash collisions)
Space: O(1) excluding output
"""
if not pattern or len(pattern) > len(text):
return []
pattern_length = len(pattern)
text_length = len(text)
BASE = 256 # Character set size
MOD = 10**9 + 7 # Large prime to reduce collisions
# Compute hash of pattern and first window
pattern_hash = 0
window_hash = 0
highest_power = 1 # BASE^(m-1) mod MOD
for i in range(pattern_length):
pattern_hash = (pattern_hash * BASE + ord(pattern[i])) % MOD
window_hash = (window_hash * BASE + ord(text[i])) % MOD
if i < pattern_length - 1:
highest_power = (highest_power * BASE) % MOD
matches = []
# Slide the window
for i in range(text_length - pattern_length + 1):
if pattern_hash == window_hash:
# Verify character by character (handle hash collision)
if text[i:i + pattern_length] == pattern:
matches.append(i)
# Compute hash for next window
if i < text_length - pattern_length:
# Remove leading char, add trailing char
window_hash = (window_hash - ord(text[i]) * highest_power) % MOD
window_hash = (window_hash * BASE + ord(text[i + pattern_length])) % MOD
window_hash = (window_hash + MOD) % MOD # Handle negative
return matches
1.5 When to Use Each Algorithm¶
| Algorithm | Best For | Pros | Cons |
|---|---|---|---|
| KMP | Single pattern search | Guaranteed O(n+m), no false positives | Need to build failure function |
| Rabin-Karp | Multiple pattern search, fingerprinting | Easy to extend, good average case | Worst case O(nm) on collisions |
| Z-Algorithm | Prefix matching, string periods | Elegant, single pass | Less intuitive |
1.6 Common Applications¶
- Find First Occurrence (LC 28): Basic substring search
- Shortest Palindrome (LC 214): KMP to find longest palindromic prefix
- Repeated Substring (LC 459): KMP failure function property
- Repeated String Match (LC 686): Rolling hash for efficiency
- Longest Happy Prefix (LC 1392): Direct KMP application
2. Base Template: Find the Index of First Occurrence (LeetCode 28)¶
Problem: Given two strings
haystackandneedle, return the index of the first occurrence ofneedleinhaystack, or -1 ifneedleis not part ofhaystack. Invariant: Use KMP failure function to skip redundant comparisons. Role: BASE TEMPLATE forStringMatchingAPI Kernel.
2.1 Why This Is The Base Template¶
LC 28 is the purest form of string matching: - Single pattern search: Find one pattern in text - First occurrence only: No need to find all matches - Foundation for variants: KMP failure function is the key building block
2.2 Implementation¶
class SolutionKMP:
def strStr(self, haystack: str, needle: str) -> int:
"""
Find first occurrence of needle in haystack using KMP.
The KMP algorithm preprocesses the pattern to build a failure function.
On mismatch, instead of restarting, we use the failure function to
skip characters that we know will match.
Time: O(m + n) where m = len(haystack), n = len(needle)
Space: O(n) for the failure function
"""
if not needle:
return 0
needle_length = len(needle)
haystack_length = len(haystack)
if needle_length > haystack_length:
return -1
# Build failure function (LPS array)
# failure[i] = length of longest proper prefix of needle[0:i+1]
# that is also a suffix
failure: list[int] = [0] * needle_length
prefix_length = 0
for i in range(1, needle_length):
# Backtrack until match or reach start
while prefix_length > 0 and needle[i] != needle[prefix_length]:
prefix_length = failure[prefix_length - 1]
if needle[i] == needle[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
# Search using failure function
needle_idx = 0
for haystack_idx in range(haystack_length):
haystack_char = haystack[haystack_idx]
# On mismatch, use failure function to skip
while needle_idx > 0 and haystack_char != needle[needle_idx]:
needle_idx = failure[needle_idx - 1]
if haystack_char == needle[needle_idx]:
needle_idx += 1
if needle_idx == needle_length:
# Found first match
return haystack_idx - needle_length + 1
return -1
2.3 Alternative: Rabin-Karp Implementation¶
class SolutionRabinKarp:
def strStr(self, haystack: str, needle: str) -> int:
"""
Find first occurrence using Rabin-Karp rolling hash.
Use polynomial rolling hash to compare windows in O(1) average.
Verify on hash match to handle collisions.
Time: O(m + n) average, O(mn) worst case
Space: O(1)
"""
if not needle:
return 0
needle_length = len(needle)
haystack_length = len(haystack)
if needle_length > haystack_length:
return -1
BASE = 256
MOD = 10**9 + 7
# Compute initial hashes
needle_hash = 0
window_hash = 0
highest_power = 1
for i in range(needle_length):
needle_hash = (needle_hash * BASE + ord(needle[i])) % MOD
window_hash = (window_hash * BASE + ord(haystack[i])) % MOD
if i < needle_length - 1:
highest_power = (highest_power * BASE) % MOD
# Slide and compare
for i in range(haystack_length - needle_length + 1):
if needle_hash == window_hash:
# Verify to avoid false positives
if haystack[i:i + needle_length] == needle:
return i
# Update hash for next window
if i < haystack_length - needle_length:
window_hash = (window_hash - ord(haystack[i]) * highest_power) % MOD
window_hash = (window_hash * BASE + ord(haystack[i + needle_length])) % MOD
window_hash = (window_hash + MOD) % MOD
return -1
2.4 Trace Example (KMP)¶
haystack = "ABABDABACDABABCABAB"
needle = "ABABCABAB"
Build failure function for "ABABCABAB":
Index: 0 1 2 3 4 5 6 7 8
Char: A B A B C A B A B
failure: 0 0 1 2 0 1 2 3 4
Search:
Position 0-3: "ABAB" matches needle[0:4]
Position 4: 'D' != 'C', backtrack to failure[3] = 2
Position 4: 'D' != 'A', backtrack to failure[1] = 0
Position 4: 'D' != 'A', needle_idx = 0
...continue...
Position 10-18: "ABABCABAB" matches completely!
Return: 10
2.5 Key Insights¶
- Failure Function: Core of KMP - tells us where to resume on mismatch
- No Backtracking in Text: Text pointer never goes backward, guaranteeing O(n)
- Self-Matching: Failure function is computed by matching pattern against itself
- Z-Algorithm Alternative: Z-array can solve same problems with different approach
3. Variant: Shortest Palindrome (LeetCode 214)¶
Problem: Given a string
s, prepend characters to make it a palindrome and return the shortest such palindrome. Invariant: Use KMP to find the longest palindromic prefix. Delta from Base: Apply KMP ons + '#' + reverse(s)to find longest prefix that is also suffix.
3.1 Key Insight¶
The shortest palindrome is formed by: 1. Finding the longest palindromic prefix of s 2. Prepending the reverse of the remaining suffix
To find longest palindromic prefix, concatenate s + '#' + reverse(s) and compute KMP failure function. The value at the last position gives us the answer.
3.2 Implementation¶
class SolutionKMP:
def shortestPalindrome(self, s: str) -> str:
"""
Find shortest palindrome by prepending characters to s.
Key insight: Longest palindromic prefix of s equals the longest
proper prefix of (s + '#' + reverse(s)) that is also a suffix.
The '#' separator prevents false matches across the boundary.
Time: O(n)
Space: O(n)
"""
if not s or len(s) <= 1:
return s
# Create concatenated string with separator
reversed_s = s[::-1]
concat = s + '#' + reversed_s
# Build failure function
concat_length = len(concat)
failure: list[int] = [0] * concat_length
prefix_length = 0
for i in range(1, concat_length):
while prefix_length > 0 and concat[i] != concat[prefix_length]:
prefix_length = failure[prefix_length - 1]
if concat[i] == concat[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
# failure[-1] = length of longest palindromic prefix
palindrome_prefix_length = failure[-1]
# Prepend reverse of non-palindromic suffix
suffix_to_prepend = reversed_s[:len(s) - palindrome_prefix_length]
return suffix_to_prepend + s
3.3 Alternative: Rolling Hash Approach¶
class SolutionRollingHash:
def shortestPalindrome(self, s: str) -> str:
"""
Use rolling hash to find longest palindromic prefix.
Compute forward and backward hashes simultaneously.
When they match at position i, s[0:i+1] is a palindrome.
Time: O(n)
Space: O(1)
"""
if not s or len(s) <= 1:
return s
BASE = 29
MOD = 10**9 + 7
forward_hash = 0
backward_hash = 0
power = 1
longest_palindrome_end = 0
for i, char in enumerate(s):
char_val = ord(char) - ord('a') + 1
# Forward hash: h = h * BASE + char
forward_hash = (forward_hash * BASE + char_val) % MOD
# Backward hash: h = h + char * BASE^i
backward_hash = (backward_hash + char_val * power) % MOD
if forward_hash == backward_hash:
# Potential palindromic prefix (may need verification)
longest_palindrome_end = i
power = (power * BASE) % MOD
# Prepend reverse of suffix after longest palindromic prefix
suffix = s[longest_palindrome_end + 1:]
return suffix[::-1] + s
3.4 Trace Example¶
s = "aacecaaa"
reversed = "aaacecaa"
concat = "aacecaaa#aaacecaa"
Failure function:
Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Char: a a c e c a a a # a a a c e c a a
LPS: 0 1 0 0 0 1 2 2 0 1 2 2 3 4 5 6 7
failure[-1] = 7
Longest palindromic prefix = "aacecaa" (length 7)
Remaining suffix = "a"
Prepend reverse of "a" = "a"
Result: "a" + "aacecaaa" = "aaacecaaa"
3.5 Key Insights¶
- Separator Is Critical: '#' prevents matching across s and reverse(s)
- Failure Function Property: failure[-1] = longest proper prefix = suffix
- Rolling Hash Alternative: No separator needed, but may have false positives
- Why Not Brute Force: Checking each prefix for palindrome would be O(nΒ²)
4. Variant: Repeated Substring Pattern (LeetCode 459)¶
Problem: Given a string
s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. Invariant: Use KMP failure function property for periodic strings. Delta from Base: Check ifn % (n - failure[n-1]) == 0and the period divides n.
4.1 Key Insight¶
For a string to be constructed from repeated substrings: - The pattern length must divide the string length evenly - Using KMP: if failure[n-1] > 0 and n % (n - failure[n-1]) == 0, the string is periodic
The minimum period length is n - failure[n-1].
4.2 Implementation¶
class SolutionKMP:
def repeatedSubstringPattern(self, s: str) -> bool:
"""
Check if string is built from repeated substrings using KMP.
Property: For periodic string, failure[n-1] = n - period_length.
So period_length = n - failure[n-1].
String is periodic if period_length divides n and period_length < n.
Time: O(n)
Space: O(n)
"""
string_length = len(s)
if string_length <= 1:
return False
# Build failure function
failure: list[int] = [0] * string_length
prefix_length = 0
for i in range(1, string_length):
while prefix_length > 0 and s[i] != s[prefix_length]:
prefix_length = failure[prefix_length - 1]
if s[i] == s[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
# Check periodicity
longest_prefix_suffix = failure[-1]
if longest_prefix_suffix == 0:
return False
period_length = string_length - longest_prefix_suffix
# Valid if period divides string length and is not the full string
return string_length % period_length == 0
4.3 Alternative: Concatenation Trick¶
class SolutionConcatenation:
def repeatedSubstringPattern(self, s: str) -> bool:
"""
Elegant trick: s + s contains s at non-boundary position iff s is periodic.
If s = "abab", then s + s = "abababab"
Remove first and last char: "bababa"
This still contains "abab" because it's periodic!
Time: O(n) with efficient string search
Space: O(n)
"""
doubled = s + s
# Remove first and last character to avoid trivial matches
return s in doubled[1:-1]
4.4 Trace Example¶
s = "abab"
Build failure function:
Index: 0 1 2 3
Char: a b a b
LPS: 0 0 1 2
failure[-1] = 2
period_length = 4 - 2 = 2
4 % 2 == 0 β
The string is built from "ab" repeated twice.
s = "abcab"
Build failure function:
Index: 0 1 2 3 4
Char: a b c a b
LPS: 0 0 0 1 2
failure[-1] = 2
period_length = 5 - 2 = 3
5 % 3 != 0 β
Not periodic.
4.5 Key Insights¶
- KMP Property: failure[n-1] reveals periodic structure
- Period Formula: period = n - failure[n-1]
- Concatenation Trick: Simpler but uses built-in string search
- Edge Cases: Single character returns False, must have period < n
5. Variant: Longest Happy Prefix (LeetCode 1392)¶
Problem: A string is called a happy prefix if it is both a prefix and suffix (excluding entire string). Return the longest happy prefix of the given string. Invariant: Direct application of KMP failure function. Delta from Base: Return s[0:failure[n-1]] directly.
5.1 Key Insight¶
This is the most direct application of the KMP failure function: - failure[n-1] gives exactly the length of the longest proper prefix that is also a suffix - Just return the first failure[n-1] characters
5.2 Implementation¶
class SolutionKMP:
def longestPrefix(self, s: str) -> str:
"""
Find longest happy prefix using KMP failure function.
The failure function at position n-1 gives exactly what we need:
the length of the longest proper prefix that is also a suffix.
Time: O(n)
Space: O(n)
"""
string_length = len(s)
if string_length <= 1:
return ""
# Build failure function
failure: list[int] = [0] * string_length
prefix_length = 0
for i in range(1, string_length):
while prefix_length > 0 and s[i] != s[prefix_length]:
prefix_length = failure[prefix_length - 1]
if s[i] == s[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
# The answer is directly failure[n-1]
happy_prefix_length = failure[-1]
return s[:happy_prefix_length]
5.3 Alternative: Rolling Hash¶
class SolutionRollingHash:
def longestPrefix(self, s: str) -> str:
"""
Find longest happy prefix using rolling hash.
Compute prefix and suffix hashes simultaneously.
Track longest match where both hashes are equal.
Time: O(n)
Space: O(1)
"""
string_length = len(s)
if string_length <= 1:
return ""
BASE = 31
MOD = 10**9 + 7
prefix_hash = 0
suffix_hash = 0
power = 1
longest_length = 0
# Compare prefix s[0:i+1] with suffix s[n-i-1:n]
for i in range(string_length - 1):
prefix_char = ord(s[i]) - ord('a') + 1
suffix_char = ord(s[string_length - 1 - i]) - ord('a') + 1
# Prefix hash: h = h * BASE + char (left to right)
prefix_hash = (prefix_hash * BASE + prefix_char) % MOD
# Suffix hash: h = h + char * power (right to left, building from end)
suffix_hash = (suffix_hash + suffix_char * power) % MOD
power = (power * BASE) % MOD
if prefix_hash == suffix_hash:
longest_length = i + 1
return s[:longest_length]
5.4 Trace Example¶
s = "ababab"
Build failure function:
Index: 0 1 2 3 4 5
Char: a b a b a b
LPS: 0 0 1 2 3 4
failure[-1] = 4
Longest happy prefix = "abab"
Verification:
- Prefix: s[0:4] = "abab" β
- Suffix: s[2:6] = "abab" β
5.5 Key Insights¶
- Direct Application: failure[-1] is exactly what we need
- Proper Prefix: Must exclude entire string (handled by failure function)
- Rolling Hash Alternative: Useful when you need to avoid preprocessing
- Empty String: Return "" if no happy prefix exists
6. Pattern Comparison Table¶
| Problem | Algorithm | Key Technique | Time | Space |
|---|---|---|---|---|
| LC 28: Find Index | KMP / Rabin-Karp | Direct pattern search | O(n+m) | O(m) |
| LC 214: Shortest Palindrome | KMP | s + '#' + reverse(s) | O(n) | O(n) |
| LC 459: Repeated Substring | KMP | Period = n - failure[n-1] | O(n) | O(n) |
| LC 1392: Longest Happy Prefix | KMP | Return s[0:failure[n-1]] | O(n) | O(n) |
6.1 Algorithm Selection Guide¶
| Use Case | Recommended Algorithm | Reason |
|---|---|---|
| Single pattern search | KMP | Guaranteed O(n+m), simple |
| Multiple pattern search | Rabin-Karp | Hash fingerprinting |
| Finding periods | KMP failure function | Built-in property |
| Palindrome problems | KMP with concatenation | Elegant reduction |
6.2 Failure Function Applications¶
| Application | How to Use failure[] |
|---|---|
| Pattern search | Backtrack on mismatch |
| String period | Period = n - failure[n-1] |
| Longest prefix=suffix | Answer = failure[n-1] |
| Palindromic prefix | Use s + '#' + rev(s) |
6.3 Time Complexity Comparison¶
| Algorithm | Preprocessing | Search | Total | Worst Case |
|---|---|---|---|---|
| Naive | O(1) | O(nm) | O(nm) | O(nm) |
| KMP | O(m) | O(n) | O(n+m) | O(n+m) |
| Rabin-Karp | O(m) | O(n) avg | O(n+m) avg | O(nm) |
| Z-Algorithm | O(m) | O(n) | O(n+m) | O(n+m) |
7. Decision Tree¶
Start: String matching/search problem?
β
βΌ
βββββββββββββββββββββ
β What's the goal? β
βββββββββββββββββββββ
β
βββββββββΌββββββββ¬ββββββββββββ
βΌ βΌ βΌ βΌ
Find Check Find Find
pattern period palindrome prefix=suffix
β β prefix β
βΌ βΌ β βΌ
Use KMP Period βΌ Direct
or R-K formula KMP+rev failure[-1]
8. Pattern Selection Guide¶
8.1 Use KMP (LC 28, 1392) when:¶
- Searching for a pattern in text
- Need guaranteed O(n+m) time
- Finding longest prefix=suffix
- Working with failure function properties
8.2 Use Rabin-Karp when:¶
- Searching multiple patterns
- Need average-case efficiency
- Hash fingerprinting is useful
- Willing to accept worst-case O(nm)
8.3 Use Period Formula (LC 459) when:¶
- Checking if string is built from repeating unit
- Finding minimum period length
- Detecting cyclic patterns
8.4 Use Concatenation Trick (LC 214) when:¶
- Finding palindromic prefixes
- Combining KMP with string reversal
- Need to find overlapping structures
9. Problem Type Recognition¶
| Keywords/Clues | Pattern |
|---|---|
| "find first occurrence" | KMP search |
| "repeated substring" | KMP period |
| "shortest palindrome" | KMP + reverse |
| "happy prefix" | KMP failure[-1] |
| "pattern matching" | KMP or Rabin-Karp |
10. Universal Templates¶
10.1 Template 1: KMP Failure Function¶
def build_failure(pattern: str) -> list[int]:
"""
Build KMP failure function (LPS array).
failure[i] = length of longest proper prefix of pattern[0:i+1]
that is also a suffix.
Time: O(m), Space: O(m)
"""
m = len(pattern)
failure = [0] * m
prefix_length = 0
for i in range(1, m):
while prefix_length > 0 and pattern[i] != pattern[prefix_length]:
prefix_length = failure[prefix_length - 1]
if pattern[i] == pattern[prefix_length]:
prefix_length += 1
failure[i] = prefix_length
return failure
Use for: LC 28, 214, 459, 1392
10.2 Template 2: KMP Search¶
def kmp_search(text: str, pattern: str) -> int:
"""
Find first occurrence of pattern in text.
Time: O(n+m), Space: O(m)
"""
if not pattern:
return 0
if len(pattern) > len(text):
return -1
failure = build_failure(pattern)
pattern_idx = 0
for text_idx, char in enumerate(text):
while pattern_idx > 0 and char != pattern[pattern_idx]:
pattern_idx = failure[pattern_idx - 1]
if char == pattern[pattern_idx]:
pattern_idx += 1
if pattern_idx == len(pattern):
return text_idx - len(pattern) + 1
return -1
Use for: LC 28, and as building block for others
10.3 Template 3: Rabin-Karp Rolling Hash¶
def rabin_karp(text: str, pattern: str) -> int:
"""
Find first occurrence using rolling hash.
Time: O(n+m) average, Space: O(1)
"""
if not pattern:
return 0
m, n = len(pattern), len(text)
if m > n:
return -1
BASE, MOD = 256, 10**9 + 7
pattern_hash = window_hash = 0
power = 1
for i in range(m):
pattern_hash = (pattern_hash * BASE + ord(pattern[i])) % MOD
window_hash = (window_hash * BASE + ord(text[i])) % MOD
if i < m - 1:
power = (power * BASE) % MOD
for i in range(n - m + 1):
if pattern_hash == window_hash and text[i:i+m] == pattern:
return i
if i < n - m:
window_hash = ((window_hash - ord(text[i]) * power) * BASE
+ ord(text[i + m])) % MOD
window_hash = (window_hash + MOD) % MOD
return -1
Use for: LC 28 (alternative), multiple pattern search
10.4 Template 4: String Period Check¶
def is_periodic(s: str) -> bool:
"""
Check if string is built from repeated substrings.
Time: O(n), Space: O(n)
"""
n = len(s)
if n <= 1:
return False
failure = build_failure(s)
period = n - failure[-1]
return failure[-1] > 0 and n % period == 0
Use for: LC 459
10.5 Template 5: Longest Prefix = Suffix¶
def longest_happy_prefix(s: str) -> str:
"""
Find longest proper prefix that is also suffix.
Time: O(n), Space: O(n)
"""
if len(s) <= 1:
return ""
failure = build_failure(s)
return s[:failure[-1]]
Use for: LC 1392
11. Quick Reference¶
| Problem Type | Template | Key Line |
|---|---|---|
| Pattern search | Template 2 | Return on pattern_idx == len(pattern) |
| Period check | Template 4 | n % (n - failure[-1]) == 0 |
| Prefix=suffix | Template 5 | Return s[:failure[-1]] |
| Palindrome prefix | Use concat s + '#' + rev(s) | failure[-1] of concat |
Document generated for NeetCode Practice Framework β API Kernel: StringMatching