The problem
You get two strings a and b. Return the smallest number of times you must repeat a (glue copies end to end) so that b becomes a substring of the result. If no number of repeats works, return -1.
Input: a = "abcd", b = "cdabcdab"
Output: 3Three copies give "abcdabcdabcd", which contains "cdabcdab" starting at index 2. Two copies ("abcdabcd") are not enough.
How to think about it
Start with brute force. Keep appending a and checking whether b is inside, forever. The question is when to stop and say -1. Without a bound, this never terminates on an impossible input.
The key insight: only two counts are worth checking. Let q be the smallest count with len(a) * q >= len(b). You need at least q copies just to have enough characters. If b fits anywhere, it starts somewhere inside the first copy of a (any later start is the same as one inside the first copy). Starting inside the first copy and running for len(b) characters, it spills into at most one extra copy. So the answer is q or q + 1, or impossible.
Why more copies never help. The repeated string is periodic with period len(a). Once you’ve tried every starting offset (q + 1 copies covers them all), adding copies only adds more of the same pattern.
Which pattern is this? Bounded string matching. The tell is “repeat a string until something matches”: find the bound where repetition stops adding new possibilities.
Approach
- Start with
s = aandcount = 1. - While
len(s) < len(b), appendaand add one tocount. Nowcountisq. - If
b in s, returncount. - If
b in s + a, returncount + 1:bstraddled one more copy. - Otherwise return
-1.
Solution
def repeatedStringMatch(a, b):
count = 1; s = a
while len(s) < len(b): # minimum copies to be long enough
s += a; count += 1
if b in s:
return count
if b in s + a: # b may straddle one more copy
return count + 1
return -1
# repeatedStringMatch("abcd", "cdabcdab") -> 3Dry run
On a = "abcd", b = "cdabcdab" (length 8):
| Step | count |
s |
Check |
|---|---|---|---|
| start | 1 | abcd |
length 4 < 8, keep going |
| loop | 2 | abcdabcd |
length 8, stop |
b in s? |
2 | abcdabcd |
False |
b in s + a? |
2 | abcdabcdabcd |
True, return 3 |
b starts at the c of the first copy, so it needs part of a third copy to finish.
Complexity
- Time: O(n + m) with a linear substring search, where
n = len(a)andm = len(b). The built string has length at mostm + 2n. CPython’sinis fast in practice; a KMP search guarantees the bound. - Space: O(n + m). For the built string.
Edge cases and pitfalls
bshorter thana: the loop doesn’t run, and you still need thes + acheck, sincebmay straddle the seam (a = "abc",b = "ca"needs 2).- Characters in
bnot inagive-1quickly:a = "abc",b = "wxyz"returns-1. - Exactly equal lengths still need the extra-copy check:
a = "abc",b = "cab"returns 2. - Looping “until it matches” with no bound hangs on impossible input. State the
q + 1bound in the interview. a = "a",b = "aa"returns 2; the loop handles it.
Related problems
- Find the Index of the First Occurrence in a String (LeetCode 28): the substring search underneath
in. - Repeated Substring Pattern (LeetCode 459): the doubled-string trick for periodic strings.
- Shortest Palindrome (LeetCode 214): another problem about gluing strings together and matching.
- 1.Find the Index of the First Occurrence in a String (LeetCode 28)
- 2.Repeated Substring Pattern (LeetCode 459)
- 3.Repeated DNA Sequences (LeetCode 187)
- 4.Repeated String Match (LeetCode 686)
- 5.Encode and Decode Strings (LeetCode 271)
- 6.Palindromic Substrings (LeetCode 647)
- 7.Reverse Words in a String (LeetCode 151)
- 8.Shortest Palindrome (LeetCode 214)
- 9.Longest Happy Prefix (LeetCode 1392)
- 10.Longest Duplicate Substring (LeetCode 1044)