686. 重复叠加字符串匹配【中等】

题目

给定两个字符串 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
 

提示:

1 <= a.length <= 104
1 <= b.length <= 104
a 和 b 由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/repeated-string-match
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的题解

class Solution {
    public int repeatedStringMatch(String a, String b) {
        if(a.equals(b) || a.indexOf(b)>=0){
            return 1;
        }else if("".equals(b)){
            return 0;
        }else if((b.length()<a.length() && a.indexOf(b)<0 && (a+a).indexOf(b)<0)){
            return -1;
        }else{
            for(char c : b.toCharArray()){
                if(a.indexOf(c+"")<0){
                    return -1;
                }
            }
        }
        int count = 2;
        StringBuilder sb = new StringBuilder(a+a);
        while(sb.toString().indexOf(b)<0){
            if(sb.toString().length()>(b.length()+a.length())) return -1;
            count++;
            sb.append(a);            
        }
        return count;
    }
}

说明

前面是基本校验:

  • a与b相同或者a已经包含b,则直接返回 1
  • b为空字符串返回 0
  • b的长度小于a的长度,那么如果a不包含b并且a重复一次后也不包含b,则返回不存在 -1
  • b中存在a不包含的字符,则返回不存在 -1

校验后直接从重复叠加a字符串2次开始进行迭代,每叠加一次进行判断叠加后的字符串是否包含b,如果包含则停止迭代;如果迭代过程中叠加字符串长度已经大于b字符串的长度加上a字符串的长度时( 当 b = a/n + a + … + a + a/n时, a叠加到长度大于等于b时,至少有一边是完全匹配,所以叠加字符串长度需要大于b+a的长度才能确定不包含),说明叠加字符串不包含b字符串,返回 -1。

执行用时:19 ms, 在所有 Java 提交中击败了 73.01% 的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了 5.54% 的用户

可以解决问题,效率一般。

官方题解

方法一: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;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/repeated-string-match/solution/zhong-fu-die-jia-zi-fu-chuan-pi-pei-by-l-vnye/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

时间复杂度:O(n + m), 其中 n 为 a 的长度,m 为 b 的长度。Rabin-Karp 算法的时间复杂度为 O(n + m)。

空间复杂度:O(1)。只需要常数空间保存参数。

执行用时:3 ms, 在所有 Java 提交中击败了 96.14% 的用户
内存消耗:36.8 MB, 在所有 Java 提交中击败了 91.57% 的用户

这个算法效率最高

方法二:Knuth-Morris-Pratt 算法

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;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/repeated-string-match/solution/zhong-fu-die-jia-zi-fu-chuan-pi-pei-by-l-vnye/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

时间复杂度:O(n + m),其中 n 为 a 的长度,m 为 b 的长度。Knuth-Morris-Pratt 算法的时间复杂度为 O(n + m)。

空间复杂度:O(m)。Knuth-Morris-Pratt 算法需要 O(m) 的空间来保存 π 数组。