LeetCode-686 重复叠加字符串匹配

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/repeated-string-match

题目描述

 

给定两个字符串 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 由小写英文字母组成

 

解题思路

本题可以拆解为两部分,首先是找到b串第一次出现的位置,然后计算a串重复的次数。

首先是a串重复次数的部分,当确定了b串第一次出现的位置index后,如果a串大小减去index后依然比b串大小大,那么a串完全可以覆盖掉b串,此时返回1.如果a串剩余的部分比b串的大小小,那么可以将重复n次的a串根据index斩头去尾然后除以a串的大小,这就是b串内部需要a串重复的次数,然后加上头与尾超出的部分两次,就可以求出n。

接下来的问题就转换为计算重复a串中b串第一次出现位置的问题。可以使用KMP算法或者RK算法来求解。

1、KMP算法。

KMP算法是利用匹配串也就是b串中重复前缀减少匹配次数的一直快速求取字符串匹配的方法。具体方法是维护一个next索引,将有共同前缀的字符跳转索引记录下来,下次就可以直接跳转到那个位置,减少重复匹配次数。具体可以参考此文

2、RK算法。

RK算法是维护一个与匹配串也就是b串大小相同的滑动窗口,如果滑动窗口和a串相同,那么就可以判断第一次出现的位置,但是比传统朴素匹配优秀的地方是,字符串会被哈希转换成一个长整形的数字,比较长整形的数字的速率是比比较字符串快的,而且窗口滑动的时候,仅需要将左部字符哈希值减去再加上右部字符的哈希值就可以,不需要重复遍历中间字符。

 

代码展示

KMP算法:

class Solution {
public:
    int KMP(string a, string b)
    {
        int m = a.size(), n = b.size();
        vector<int> viNext(n, 0);
        for(int i = 1, j = 0; i < n; i++)
        {
            while(b[i] != b[j] && j > 0)
            {
                j = viNext[j - 1];
            }
            if(b[i] != b[j])
            {
                viNext[i] = 0;
            }
            else
            {
                viNext[i] = j + 1;
                j++;
            }
        }
        for(int i = 0, j = 0; i < m + n; i++)
        {
            while(a[i % m] != b[j] && j > 0)
            {
                j = viNext[j - 1];
            }
            if(a[i % m] != b[j])
            {
                j = 0;
            }
            else
            {
                j++;
            }
            if(j == n)
            {
                return i - n + 1;
            }
        }
        return -1;
    }
    int repeatedStringMatch(string a, string b) {
        int iRet = KMP(a, b);
        if(iRet != -1)
        {
            if(a.size() - iRet >= b.size())
            {
                iRet = 1;
            }
            else
            {
                iRet = 2 + (b.size() - a.size() + iRet - 1 ) / a.size();
            }
        }
        
        return iRet;
    }
};

 

RK算法:

 

class Solution {
public:
    int RK(string a, string b)
    {
        int m = a.size(), n = b.size();
        if(n == 0) return 0;

        long long k1 = 1e9 + 7;
        long long k2 = 1337;
        srand((unsigned)time(NULL));
        long long kMod1 = rand() % k1 + k1;
        long long kMod2 = rand() % k2 + k2;
        long long llb = 0;
        for(auto c: b)
        {
            llb = (llb * kMod2 + c) % kMod1;
        }
        long long lla = 0, llTemp = 1;
        for(int i = 0; i < m + n - 1; i++)
        {
            lla = (lla * kMod2 + a[i % m]) % kMod1;
            if(i < n - 1)
            {
                llTemp = (llTemp * kMod2) % kMod1;
            }
            else
            {
                if(lla == llb)
                {
                    return i - n + 1;
                }
                lla = (lla - a[(i - n + 1) % m] * llTemp) % kMod1;
                lla = (lla + kMod1) % kMod1;
            }
        }

        return -1;
    }
    int repeatedStringMatch(string a, string b) {
        int iRet = RK(a, b);
        if(iRet != -1)
        {
            if(a.size() - iRet >= b.size())
            {
                iRet = 1;
            }
            else
            {
                iRet = 2 + (b.size() - a.size() + iRet - 1 ) / a.size();
            }
        }
        
        return iRet;
    }
};

 

 

 

运行结果

LeetCode-686 重复叠加字符串匹配

 

上一篇:leetcode_645. 错误的集合


下一篇:112. Leetcode 673. 最长递增子序列的个数 (动态规划-子序列问题)