Repeated String Match — Solution in Python, Java & C++
Repeated String Match solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A medium problem.
Repeated String Match is a medium coding problem on strings, rolling hash and string matching, asked in Amazon and Google interviews.
- Difficulty: Medium
- Topics: Strings, Rolling Hash, String Matching
- Asked at: Amazon, Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
Given two strings a and b, return the minimum number of times a must be repeated (written end to end) so that b becomes a substring of the result. If no number of repetitions works, return -1.
Repeating "abc" zero times gives "", once gives "abc", twice gives "abcabc".
Example 1
Input: a = "kai", b = "aikaik"
Output: 3
Explanation: `"kaikaikai"` contains `"aikaik"` starting at index 1; `"kaikai"` is too short to.
Example 2
Input: a = "z", b = "zzzz"
Output: 4
Example 3
Input: a = "abc", b = "acb"
Output: -1
Explanation: In any repetition of `a`, the letter after `a` is always `b`.
Constraints
1 <= a.length, b.length <= 10^4a and b consist of lowercase English letters
Hints for Repeated String Match
Hint 1
The repeated string must at least be as long as
b, so you need at leastq = ceil(|b| / |a|)copies.Hint 2
If
boccurs at all, it starts inside the first copy ofa. Starting there, it ends withinq + 1copies.Hint 3
So only two candidates matter: check
arepeatedqtimes, thenq + 1times; otherwise answer-1.
How to solve Repeated String Match
Any occurrence of b in a long repetition of a can be shifted left by whole copies until it starts in the first copy, and from there it spans at most q + 1 copies, where q = ceil(|b| / |a|).
Approach
- Build
sby appendingauntil|s| >= |b|; letqbe the number of copies used. - If
bis a substring ofs, returnq. - Append one more
a; ifbis now a substring, returnq + 1. - Otherwise return
-1.
Why it works
Fewer than q copies are shorter than b, so q is a lower bound. If b occurs in some repetition, shift the occurrence left by multiples of |a| (the string is periodic) until it starts at an offset o < |a|; it then ends before o + |b| < |a| + q·|a|, inside q + 1 copies. Hence if neither candidate contains b, no repetition does.
Complexity
- Time —
O(|a| + |b|) with a linear-time matcher (KMP / Z); a library substring search is fine in practice - Space —
O(|a| + |b|)
Pitfalls
- Checking only
qcopies misses cases likea = "abcd",b = "cdab", which need one extra copy. - Looping on more and more copies without a bound never terminates when the answer is
-1.
Reference solution
Python
def repeatedStringMatch(a: str, b: str) -> int:
q = -(-len(b) // len(a)) # ceil
s = a * q
if b in s:
return q
if b in s + a:
return q + 1
return -1Java
public static int repeatedStringMatch(String a, String b) {
int q = (b.length() + a.length() - 1) / a.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < q; i++) sb.append(a);
if (sb.indexOf(b) != -1) return q;
sb.append(a);
if (sb.indexOf(b) != -1) return q + 1;
return -1;
}C++
int repeatedStringMatch(string a, string b) {
int q = ((int) b.size() + (int) a.size() - 1) / (int) a.size();
string s;
for (int i = 0; i < q; i++) s += a;
if (s.find(b) != string::npos) return q;
s += a;
if (s.find(b) != string::npos) return q + 1;
return -1;
}JavaScript
var repeatedStringMatch = function(a, b) {
var q = Math.ceil(b.length / a.length);
var s = "";
for (var i = 0; i < q; i++) s += a;
if (s.indexOf(b) !== -1) return q;
if ((s + a).indexOf(b) !== -1) return q + 1;
return -1;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings