[Medium] 686. Repeated String Match
Given two strings a and b, return the minimum number of times you should repeat string a so that string b is a substring of it. If it is impossible for b to be a substring of a after repeating it, return -1.
Notice: String "abc" repeated 0 times is "", repeated 1 time is "abc", and repeated 2 times is "abcabc".
Thinking Process
Given two strings a and b, return the minimum number of times you should repeat string a so that string b is a substring of it. If it is impossible for b to be a substring of a after repeating it, return -1.
Notice: String "abc" repeated 0 times is "", repeated 1 time is "abc", and repeated 2 times is "abcabc".
- Identify the pattern from constraints (sorted? graph? optimal substructure?).
- Write brute force first mentally, then optimize the bottleneck.
- Verify edge cases: empty input, single element, duplicates.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Brute force (this problem) | Often O(n^2) or O(2^n) | O(n) | Baseline; clarifies the optimization target |
| Sort + scan | O(n log n) | O(1) | Pairs, intervals, greedy ordering |
| Hash map / set | O(n) | O(n) | Frequency, membership, two-sum style |
| Single-pass linear | O(n) | O(1) | Two pointers, sliding window, Kadane |
Examples
Example 1:
Input: a = "abcd", b = "cdabcdab"
Output: 3
Explanation: We return 3 because by repeating a three times "abcdabcdabcd", b is a substring of it.
Example 2:
Input: a = "a", b = "aa"
Output: 2
Example 3:
Input: a = "a", b = "a"
Output: 1
Example 4:
Input: a = "abc", b = "wxyz"
Output: -1
Constraints
1 <= a.length, b.length <= 10^4aandbconsist of lowercase English letters.
String Matching Algorithms
Knuth-Morris-Pratt (KMP) Algorithm
KMP is an efficient string-searching algorithm that preprocesses the pattern to create a prefix function (also called LPS - Longest Proper Prefix which is also a Suffix). This allows skipping unnecessary comparisons.
Key Concepts:
- Prefix Function (π/LPS): For each position
iin pattern,π[i]is the length of the longest proper prefix that is also a suffix ofpattern[0..i] - No Backtracking: When a mismatch occurs, we don’t reset to the beginning but use the prefix function to determine the next position
- Time Complexity: O(n + m) where n = text length, m = pattern length
How KMP Works:
- Preprocessing Phase: Build prefix function for pattern
- For each position, find longest prefix-suffix match
- Use previous values to compute current value efficiently
- Search Phase: Match pattern in text
- Compare characters from left to right
- On mismatch, use prefix function to skip ahead
- Never backtrack in text
Prefix Function Example:
For pattern "ababaca":
Pattern: a b a b a c a
Index: 0 1 2 3 4 5 6
π[i]: 0 0 1 2 3 0 1
Explanation:
- π[0] = 0 (no proper prefix)
- π[1] = 0 ("ab" has no prefix-suffix match)
- π[2] = 1 ("aba" has "a" as prefix-suffix)
- π[3] = 2 ("abab" has "ab" as prefix-suffix)
- π[4] = 3 ("ababa" has "aba" as prefix-suffix)
- π[5] = 0 ("ababac" has no prefix-suffix match)
- π[6] = 1 ("ababaca" has "a" as prefix-suffix)
Rabin-Karp Algorithm
Rabin-Karp uses rolling hash to efficiently compute hash values for substrings. It compares hash values first, then verifies with character-by-character comparison if hashes match.
Key Concepts:
- Rolling Hash: Compute hash of substring in O(1) time using previous hash
- Hash Function: Use polynomial rolling hash:
hash = (hash * base + char) % mod - Collision Handling: When hashes match, verify with actual string comparison
- Time Complexity: Average O(n + m), worst case O(n × m) if many hash collisions
How Rabin-Karp Works:
- Precompute Pattern Hash: Calculate hash of pattern string
- Rolling Hash in Text:
- Compute hash of first window
- Slide window and update hash in O(1)
- Compare hashes, verify if match
- Base and Modulo: Use large base and prime modulo to reduce collisions
Rolling Hash Formula:
For substring s[i..i+m-1]:
hash = (s[i] * base^(m-1) + s[i+1] * base^(m-2) + ... + s[i+m-1]) % mod
To slide window from i to i+1:
new_hash = ((old_hash - s[i] * base^(m-1)) * base + s[i+m]) % mod
KMP Template
Here’s the general template for KMP algorithm:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if needle == "":
return 0
n, m = len(haystack), len(needle)
# build lps (prefix function)
lps = [0] * m
j = 0
for i in range(1, m):
while j > 0 and needle[i] != needle[j]:
j = lps[j - 1]
if needle[i] == needle[j]:
j += 1
lps[i] = j
# search
j = 0
for i in range(n):
while j > 0 and haystack[i] != needle[j]:
j = lps[j - 1]
if haystack[i] == needle[j]:
j += 1
if j == m:
return i - m + 1
return -1
Key Template Components:
- Prefix Function (π/LPS):
pi[i]= longest prefix-suffix length forpattern[0..i]- Built in O(m) time
- Search Algorithm:
- No backtracking in text
- Use prefix function to skip on mismatch
- Time complexity: O(n + m)
- Circular Matching:
- Use
i % nfor circular text - Adjust loop condition:
i - j < n
- Use
Rabin-Karp Template
Here’s the general template for Rabin-Karp algorithm:
class Solution:
BASE = 256
MOD = 10**9 + 7
def computeHash(self, s, start, length):
h = 0
for i in range(length):
h = (h * self.BASE + ord(s[start + i])) % self.MOD
return h
def updateHash(self, oldHash, remove, add, power):
oldHash = (oldHash - remove * power) % self.MOD
oldHash = (oldHash * self.BASE + add) % self.MOD
return oldHash
def verifyMatch(self, text, start, pattern):
for i in range(len(pattern)):
if text[start + i] != pattern[i]:
return False
return True
def repeatedStringMatch(self, a: str, b: str) -> int:
# simple correct solution (recommended)
repeat = -(-len(b) // len(a)) # ceil
s = a * repeat
if b in s:
return repeat
if b in s + a:
return repeat + 1
return -1
Key Template Components:
- Hash Function:
- Polynomial rolling hash:
hash = (hash * BASE + char) % MOD - BASE = 256 (for ASCII), MOD = large prime
- Polynomial rolling hash:
- Rolling Hash:
- Update hash in O(1) when sliding window
- Remove left char, add right char
- Collision Handling:
- Always verify hash matches with actual string comparison
- Prevents false positives
Complexity
Solution 1: KMP
Time Complexity: O(n + m)
- Prefix function: O(m) - build LPS array
- Search phase: O(n) - each character visited at most twice
- Total: O(n + m) where n = a.length, m = b.length
Space Complexity: O(m)
- Prefix function array: O(m)
- Total: O(m)
Solution 2: Rabin-Karp
Time Complexity: O(n + m) average, O(n × m) worst case
- Hash computation: O(m) for pattern
- Rolling hash: O(n) for text (O(1) per window)
- Verification: O(m) per hash match (rare collisions)
- Total: O(n + m) average, O(n × m) worst case with many collisions
Space Complexity: O(1)
- Hash variables: O(1)
- Total: O(1) excluding input strings
Key Points
- KMP is Optimal: Guaranteed O(n + m) time, no worst-case degradation
- Rabin-Karp: Average O(n + m), but can degrade with hash collisions
- Circular Matching: Use modulo arithmetic for repeated strings
- Minimum Repetitions: At least
⌈b.length / a.length⌉, at most⌈b.length / a.length⌉ + 1 - Prefix Function: Key to KMP’s efficiency - avoids backtracking
- Rolling Hash: Key to Rabin-Karp’s efficiency - O(1) hash updates
Comparison: KMP vs Rabin-Karp
| Aspect | KMP | Rabin-Karp |
|---|---|---|
| Time Complexity | O(n + m) guaranteed | O(n + m) average, O(n × m) worst |
| Space Complexity | O(m) | O(1) |
| Preprocessing | O(m) for prefix function | O(m) for pattern hash |
| Backtracking | None (no text backtracking) | None (sliding window) |
| Collision Handling | Not needed | Required (verify matches) |
| Implementation | More complex | Simpler |
| Recommended | ✅ Yes (guaranteed performance) | ⚠️ Good for average case |
Key Takeaways
- Notice:** String
"abc"repeated 0 times is"", repeated 1 time is"abc", and repeated 2 times is"abcabc". - Identify the pattern from constraints (sorted? graph? optimal substructure?).
- Write brute force first mentally, then optimize the bottleneck.
References
- LC 686: Repeated String Match on LeetCode
- LeetCode Discuss — LC 686: Repeated String Match
- LeetCode Editorial (may require premium)
Common Mistakes
- Skipping edge cases (empty input, single element, boundaries).
- Off-by-one errors in loops and index ranges.
- Forgetting to handle the case when no valid answer exists.
Related Problems
- 28. Find the Index of the First Occurrence in a String - KMP application
- 214. Shortest Palindrome - KMP for palindrome
- 1392. Longest Happy Prefix - Prefix function
- 187. Repeated DNA Sequences - Rolling hash
Tags
String Matching, KMP, Knuth-Morris-Pratt, Rabin-Karp, Rolling Hash, Prefix Function, Medium