Description
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".
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
Constraints:
aandbconsist of lowercase English letters.
Approach 1: Length-Bound Simulation ( Time, Space)
Intuition
- Repeat string
auntil its total length is at least as long as stringb. Keep track of the repeat count. - If
bis a substring of this repeateda, return the current count. - Otherwise, append
aone additional time to cover potential boundary overlaps (wherebstarts near the end ofaand wraps around to the beginning ofa). Check again. - If
bis still not found after this single extra repeat, it will never be possible, so return-1.
class Solution {
public int repeatedStringMatch(String a, String b) {
StringBuilder sb = new StringBuilder();
int count = 0;
// Step 1: Append 'a' until string length >= b.length()
while (sb.length() < b.length()) {
sb.append(a);
count++;
}
// Step 2: Check if b is a substring
if (sb.indexOf(b) != -1) {
return count;
}
// Step 3: Append 'a' one more time for boundary overlaps
sb.append(a);
count++;
if (sb.indexOf(b) != -1) {
return count;
}
return -1;
}
}
Complexity
- Time Complexity: — Where and . Constructing the string takes , while built-in
indexOfpattern matching takes worst-case time. - Space Complexity: — To store the repeated string in memory.
Approach 2: KMP Algorithm (Optimal — Time, Space)
Intuition
To guarantee linear time complexity without relying on generic substring search:
- Construct the Longest Prefix Suffix (LPS) array for pattern
b. - Search for pattern
binsideatreated as an infinite stream (using modular indexingi % a.length()). - Maintain a counter of how many total characters in
ahave been consumed. Once the entire patternbis matched, calculate the minimum number of repeats ofaneeded to cover that character range.
class Solution {
public int repeatedStringMatch(String a, String b) {
int n = a.length();
int m = b.length();
// Step 1: Compute LPS array for string b
int[] lps = computeLPS(b);
// Step 2: KMP Search over virtual repeated string 'a'
int i = 0; // Pointer for string a (virtual stream)
int j = 0; // Pointer for pattern b
// Maximum character checks needed to cover all overlap positions
while (i < n + m + n) {
if (a.charAt(i % n) == b.charAt(j)) {
i++;
j++;
if (j == m) {
// Match found! Calculate total repeats used
return (int) Math.ceil((double) i / n);
}
} else {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
return -1;
}
private int[] computeLPS(String pattern) {
int m = pattern.length();
int[] lps = new int[m];
int len = 0;
int i = 1;
while (i < m) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
}
Complexity
- Time Complexity: — Precomputing LPS takes and the KMP search traverses at most character checks in time.
- Space Complexity: — For the LPS lookup table of pattern
b.
Easy Memory Rule
“Repeat
auntil length . CheckindexOf(b)now, and check once more with one extraa. If neither works, return-1!”
Prefix
A prefix is a part of a string that starts from the beginning of the string.
Example: ABCDE
Prefixes:
A, AB, ABC, ABCD, ABCDE
Suffix
A suffix is a part of a string that ends at the end of the string.
Example: ABCDE
Suffixes:
E, DE, CDE, BCDE, ABCDE
Proper Prefix / Suffix
A proper prefix/suffix is a prefix or suffix that is not the entire string.
For ABCDE:
ABCDE→ prefix and suffix, but not a proper prefix/suffixABC→ proper prefixCDE→ proper suffix
We don’t consider the entire string because otherwise every string would always have itself as both a prefix and suffix.
LPS (Longest Prefix Suffix)
LPS = Longest Proper Prefix which is also a Suffix.
In other words:
Find the longest part that appears at both the beginning and end of the string, excluding the whole string.
Example: ABAB
-
Prefixes:
A,AB,ABA -
Suffixes:
B,AB,BAB -
Common:
AB -
Therefore, LPS = 2 (length of
AB) -
We have a prefix-window on the left and a suffix-window ending at
i;lenis their current matching length. If the next characters match, extend the windows; if they don’t, shrink to the largest smaller prefix that can still overlap.
What is LPS?
LPS stands for Longest Prefix Suffix.
In the KMP algorithm, lps[i] stores the length of the longest proper prefix of the substring pattern[0...i] that is also a suffix of that same substring.
- Prefix: A substring starting at index
0. - Suffix: A substring ending at index
i. - Proper Prefix: A prefix that is not equal to the whole string itself.
Example: pattern = "ABABC"
- For
"A"(len1): Prefix ="", Suffix =""lps[0] = 0 - For
"AB"(len2): Prefix ="A", Suffix ="B"lps[1] = 0 - For
"ABA"(len3): Prefix ="A", Suffix ="A"lps[2] = 1 - For
"ABAB"(len4): Prefix ="AB", Suffix ="AB"lps[3] = 2 - For
"ABABC"(len5): No matching prefix/suffixlps[4] = 0
How to Compute LPS ( Time)
We maintain two pointers:
i: Moves forward through the string (starts at1).len: Stores the length of the previous longest prefix-suffix (starts at0).
Algorithm Logic:
- Set
lps[0] = 0. - If
pattern[i] == pattern[len]:len++lps[i] = leni++
- If
pattern[i] != pattern[len]:- If
len != 0: Backtracklen = lps[len - 1](do not incrementi). - If
len == 0: Setlps[i] = 0, theni++.
- If
KMP
while (i < text.length()) {
if (text.charAt(i) == pattern.charAt(j)) {
i++;
j++;
if (j == pattern.length()) {
// pattern found
}
} else {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}