目录
重复叠加字符串匹配
描述
给定两个字符串 a 和 b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 的子串,如果不存在则返回 -1。
注意:字符串 "abc" 重复叠加 0 次是 "",重复叠加 1 次是 "abc",重复叠加 2 次是 "abcabc"。
示例 1
输入:a = "abcd", b = "cdabcdab" 输出:3 解释:a 重复叠加三遍后为 "abcdabcdabcd", 此时 b 是其子串。
示例 2
输入:a = "a", b = "aa" 输出:2
示例 3
输入:a = "a", b = "a" 输出:1
示例 4
输入:a = "abc", b = "wxyz" 输出:-1
提示
- a 和 b 由小写英文字母组成
方法一:暴力
class Solution {
public int repeatedStringMatch(String a, String b) {
StringBuilder sb = new StringBuilder();
int ans = 0;
int max = 2 * a.length() + b.length();
while (sb.length() < max) {
sb.append(a);
ans++;
if (sb.toString().indexOf(b) != -1) return ans;
}
return -1;
}
}
方法二:Rabin-Karp 算法
命题「存在重复叠加字符串 ,使得字符串 b 成为叠加后的字符串
的子串」等价于「字符串 bb 成为无限重复叠加字符串
的子串」。
而后者成立的前提是任一 以 b 为前缀,即 b 可以从第一个叠加的 a 开始匹配成功。
因此我们可以分两种情况:
- b 可以从第一个叠加的 a 开始匹配成功,则明显匹配的下标越小,最终需要的叠加数目 kk 越小,记成功匹配的最小下标为
,于是:
- b 无法从第一个叠加的 a 开始匹配成功,说明不存在重复叠加字符串
,使得字符串 b 成为叠加后的字符串
的子串
在应用 Rabin-Karp 算法时,被匹配字符串是循环叠加的字符串,所以下标要进行取余操作,并且匹配终止的条件为 b 开始匹配的位置超过第一个叠加的 a。我们采用随机数来生成 Rabin-Karp 算法的哈希函数,希望避免后续哈希冲突的发生。
class Solution {
static final int kMod1 = 1000000007;
static final int kMod2 = 1337;
public int repeatedStringMatch(String a, String b) {
int an = a.length(), bn = b.length();
int index = strStr(a, b);
if (index == -1) {
return -1;
}
if (an - index >= bn) {
return 1;
}
return (bn + index - an - 1) / an + 2;
}
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
if (m == 0) {
return 0;
}
int k1 = 1000000009;
int k2 = 1337;
Random random = new Random();
int kMod1 = random.nextInt(k1) + k1;
int kMod2 = random.nextInt(k2) + k2;
long hashNeedle = 0;
for (int i = 0; i < m; i++) {
char c = needle.charAt(i);
hashNeedle = (hashNeedle * kMod2 + c) % kMod1;
}
long hashHaystack = 0, extra = 1;
for (int i = 0; i < m - 1; i++) {
hashHaystack = (hashHaystack * kMod2 + haystack.charAt(i % n)) % kMod1;
extra = (extra * kMod2) % kMod1;
}
for (int i = m - 1; (i - m + 1) < n; i++) {
hashHaystack = (hashHaystack * kMod2 + haystack.charAt(i % n)) % kMod1;
if (hashHaystack == hashNeedle) {
return i - m + 1;
}
hashHaystack = (hashHaystack - extra * haystack.charAt((i - m + 1) % n)) % kMod1;
hashHaystack = (hashHaystack + kMod1) % kMod1;
}
return -1;
}
}
方法三:KMP算法
class Solution {
public int repeatedStringMatch(String a, String b) {
int an = a.length(), bn = b.length();
int index = strStr(a, b);
if (index == -1) {
return -1;
}
if (an - index >= bn) {
return 1;
}
return (bn + index - an - 1) / an + 2;
}
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
if (m == 0) {
return 0;
}
int[] pi = new int[m];
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
j = pi[j - 1];
}
if (needle.charAt(i) == needle.charAt(j)) {
j++;
}
pi[i] = j;
}
for (int i = 0, j = 0; i - j < n; i++) { // b 开始匹配的位置是否超过第一个叠加的 a
while (j > 0 && haystack.charAt(i % n) != needle.charAt(j)) { // haystack 是循环叠加的字符串,所以取 i % n
j = pi[j - 1];
}
if (haystack.charAt(i % n) == needle.charAt(j)) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}
}