来源:力扣(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; } };
运行结果