由 n 個連接的字符串 s 組成字符串 S,記作 S = [s,n]。例如,[“abc”,3]=“abcabcabc”。
如果我們可以從 s2 中刪除某些字符使其變爲 s1,則稱字符串 s1 可以從字符串 s2 獲得。例如,根據定義,“abc” 可以從 “abdbec” 獲得,但不能從 “acbbe” 獲得。
現在給你兩個非空字符串 s1 和 s2(每個最多 100 個字符長)和兩個整數 0 ≤ n1 ≤ 106 和 1 ≤ n2 ≤ 106。現在考慮字符串 S1 和 S2,其中 S1=[s1,n1] 、S2=[s2,n2] 。
請你找出一個可以滿足使[S2,M] 從 S1 獲得的最大整數 M 。
示例:
輸入:
s1 =“acb”,n1 = 4
s2 =“ab”,n2 = 2
返回:
2
/*
方法一:找出循環節
*/
class Solution {
public:
int getMaxRepetitions(string s1, int n1, string s2, int n2) {
if (n1 == 0) {
return 0;
}
int s1cnt = 0, index = 0, s2cnt = 0;
/*
recall是我們用來找循環節的變量,它是一個哈希映射
我們如何找循環節?假設我們遍歷了s1cnt個s1,此時匹配到了第s2cnt個s2中的第index個字符
如果我們之前遍歷了s1cnt'個s1時,匹配到的是第s2cnt'個s2中同樣的第index個字符,那麼就有循環節了
我們用(s1cnt',s2cnt',index)和(s1cnt,s2cnt,index)表示兩次包含相同index的匹配結果
那麼哈希映射中的鍵就是index,值就是(s1cnt',s2cnt')這個二元組
循環節就是:
-前s1cnt'個s1包含了s2cnt'個s2;
-以後的每(s1cnt - s1cnt')個s1包含了(s2cnt-s2cnt')個s2
那麼還會剩下(n1 - s1cnt)% (s1cnt - s1cnt')個s1,我們對這些與s2進行暴力匹配
注意s2要從第index個字符開始匹配
*/
unordered_map<int, pair<int, int>> recall;
pair<int, int> preLoop, inLoop;
while (true) {
// 我們遍歷一個s1,看看能不能找不到循環節
++s1cnt;
for (char ch: s1) {
if (ch == s2[index]) {
index += 1;
if (index == s2.size()) {
++s2cnt;
index = 0;
}
}
}
// 沒有找到循環節,所有的s1就用完了
if (s1cnt == n1) {
return s2cnt / n2;
}
// 出現了之前的index,表示找到了循環節
if (recall.count(index)) {
auto [s1cntPrime, s2cntPrime] = recall[index];
// 前s1cnt'個s1包含了s2cnt'個s2
preLoop = {s1cntPrime, s2cntPrime};
// 以後的每(s1cnt - s2cnt')個s1包含了(s2cnt - s2cnt')個s2
inLoop = {s1cnt - s1cntPrime, s2cnt - s2cntPrime};
break;
}
else {
recall[index] = {s1cnt, s2cnt};
}
}
// ans存儲的S1包含的s2數量,考慮到之前的preLoop和inLoop
int ans = preLoop.second + (n1 - preLoop.first) / inLoop.first * inLoop.second;
// S1的末尾還剩下一些s1(數量不足以構成循環節),我們暴力進行匹配
int rest = (n1 - preLoop.first) % inLoop.first;
for (int i = 0; i < rest; i++) {
for (char ch : s1) {
if (ch == s2[index]) {
index++;
if (index == s2.size()) {
ans++;
index = 0;
}
}
}
}
// S1包含ans個s2,那麼久包含ans / n2個S2;
return ans / n2;
}
};