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; } };
執行結果