-
Notifications
You must be signed in to change notification settings - Fork 43
/
RepeatedStringMatch.java
190 lines (169 loc) · 6.69 KB
/
RepeatedStringMatch.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package LeetCodeJava.String;
// https://leetcode.com/problems/repeated-string-match/description/
import java.math.BigInteger;
/**
* 686. Repeated String Match
* Solved
* Medium
* Topics
* Companies
* 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:
*
* 1 <= a.length, b.length <= 104
* a and b consist of lowercase English letters.
*
*/
public class RepeatedStringMatch {
// V0
// IDEA : BRUTE FORCE + StringBuilder (poor performance, check V0-1 for optimization)
public int repeatedStringMatch(String a, String b) {
// edge case
if (a.contains(b)){
return 1;
}
int cnt = 1;
StringBuilder sb = new StringBuilder();
sb.append(a);
// TODO : check ??
// 1 <= a.length, b.length <= 10^4
while (sb.toString().length() <= 10000){
if (sb.toString().contains(b)){
return cnt;
}
sb.append(a);
cnt += 1;
}
return -1;
}
// V0-1
// IDEA : MATH + StringBuilder (offered by gpt)
public int repeatedStringMatch_0_1(String a, String b) {
// Calculate the minimum number of times to repeat 'a'
/**
* NOTE !!!
*
* The line int repeatCount = (int) Math.ceil((double) b.length() / a.length());
* is used to determine the minimum number of times the string
* a needs to be repeated so that it could potentially contain the string b.
* Here’s a breakdown of why this calculation is necessary:
*
*
* Explanation of the Calculation:
* 1. Understanding String Lengths:
* • The length of a indicates how many characters are in the string a.
* • The length of b indicates how many characters are in the string b.
*
* 2. Division to Find Minimum Repeats:
* • By dividing b.length() by a.length(), we get the number of full a strings needed to cover the length of b.
* • This division gives a floating-point result, which might not be a whole number if b is not an exact multiple of a.
*
*
* 3. Using Math.ceil:
* • The Math.ceil function is used to round up to the nearest whole number. This is important because even if b needs only a fraction of another a to be fully included, we still need to count that additional occurrence.
* • For example, if b is 7 characters long and a is 3 characters long, b.length() / a.length() would yield approximately 2.33. Using Math.ceil gives us 3, meaning we need to repeat a at least 3 times to potentially cover b.
*
* 4. Importance in the Loop:
* • By calculating repeatCount this way, we ensure that we do not perform unnecessary iterations. We only attempt to find b in a sufficient number of repetitions of a, making the algorithm more efficient.
*
*
*
* Summary
*
* This calculation is a crucial optimization in the algorithm that reduces the number of string concatenations and checks, ensuring the solution is efficient even for larger strings.
*
* Example:
*
* If we have:
*
* • a = "abc" (length 3)
* • b = "cabcab" (length 6)
*
* Calculating:
*
* • b.length() / a.length() = 6 / 3 = 2
* • Math.ceil(2) = 2 (but in this case, we need at least 3 repetitions of a to guarantee b can fit, so repeatCount might be incremented to 3 in the context of the algorithm).
*
* This ensures that we account for all potential overlaps in the string repetitions.
*/
int repeatCount = (int) Math.ceil((double) b.length() / a.length());
// Build the initial string
StringBuilder sb = new StringBuilder(a);
// Check for the first string length after repetitions
for (int i = 1; i < repeatCount + 2; i++) {
if (sb.indexOf(b) != -1) {
return i; // Return the current repeat count
}
sb.append(a); // Append 'a' again
}
return -1; // 'b' was not found
}
// V1-1
// IDEA : Ad-Hoc
// https://leetcode.com/problems/repeated-string-match/editorial/
public int repeatedStringMatch_1_1(String A, String B) {
int q = 1;
StringBuilder S = new StringBuilder(A);
for (; S.length() < B.length(); q++) S.append(A);
if (S.indexOf(B) >= 0) return q;
if (S.append(A).indexOf(B) >= 0) return q+1;
return -1;
}
// V1-2
// IDEA : Rabin-Karp (Rolling Hash)
// https://leetcode.com/problems/repeated-string-match/editorial/
public boolean check(int index, String A, String B) {
for (int i = 0; i < B.length(); i++) {
if (A.charAt((i + index) % A.length()) != B.charAt(i)) {
return false;
}
}
return true;
}
public int repeatedStringMatch_1_2(String A, String B) {
int q = (B.length() - 1) / A.length() + 1;
int p = 113, MOD = 1_000_000_007;
int pInv = BigInteger.valueOf(p).modInverse(BigInteger.valueOf(MOD)).intValue();
long bHash = 0, power = 1;
for (int i = 0; i < B.length(); i++) {
bHash += power * B.codePointAt(i);
bHash %= MOD;
power = (power * p) % MOD;
}
long aHash = 0; power = 1;
for (int i = 0; i < B.length(); i++) {
aHash += power * A.codePointAt(i % A.length());
aHash %= MOD;
power = (power * p) % MOD;
}
if (aHash == bHash && check(0, A, B)) return q;
power = (power * pInv) % MOD;
for (int i = B.length(); i < (q + 1) * A.length(); i++) {
aHash -= A.codePointAt((i - B.length()) % A.length());
aHash *= pInv;
aHash += power * A.codePointAt(i % A.length());
aHash %= MOD;
if (aHash == bHash && check(i - B.length() + 1, A, B)) {
return i < q * A.length() ? q : q + 1;
}
}
return -1;
}
// V2
}