持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情
定义 str = [s, n]
表示 str
由 n
个字符串 s
连接构成。
- 例如,
str == ["abc", 3] =="abcabcabc"
。
如果可以从 s2
中删除某些字符使其变为 s1
,则称字符串 s1
可以从字符串 s2
获得。
- 例如,根据定义,
s1 = "abc"
可以从s2 = "ab***dbe***c"
获得,仅需要删除加粗且用斜体标识的字符。
现在给你两个字符串 s1
和 s2
和两个整数 n1
和 n2
。由此构造得到两个字符串,其中 str1 = [s1, n1]、str2 = [s2, n2]
。
请你找出一个最大整数 m
,以满足 str = [str2, m]
可以从 str1
获得。
示例 1:
输入:s1 = "acb", n1 = 4, s2 = "ab", n2 = 2
输出:2
示例 2:
输入:s1 = "acb", n1 = 1, s2 = "acb", n2 = 1
输出:1
找出循环节
由于题目中的 n1
和 n2
都很大,因此我们无法真正把 S1 = [s1, n1]
和 S2 = [s2, n2]
都显式地表示出来。由于这两个字符串都是不断循环的,因此我们可以考虑找出 S2
在 S1
中出现的循环节,如果我们找到了循环节,那么我们就可以很快算出 S2
在 S1
中出现了多少次了。
算法
我们设计一个哈希表 recall
:哈希表 recall
以 s2
字符串的下标 index
为索引,存储匹配至第 s1cnt
个 s1
的末尾,当前匹配到第 s2cnt
个 s2
中的第 index
个字符时, 已经匹配过的s1
的个数 s1cnt
和 s2
的个数 s2cnt
。
我们在每次遍历至 s1
的末尾时根据当前匹配到的 s2
中的位置 index
查看哈希表中的对应位置,如果哈希表中对应的位置 index
已经存储元素,则说明我们找到了循环节。循环节的长度可以用当前已经匹配的 s1
与 s2
的数量减去上次出现时经过的数量(即哈希表中存储的值)来得到。
然后我们就可以通过简单的运算求出所有构成循环节的 s2
的数量,对于不参与循环节部分的 s1
,直接遍历计算即可,具体实现以及一些细节边界的处理请看下文的代码。
var getMaxRepetitions = function (s1, n1, s2, n2) {
let counts1 = 0//s1计数
let counts2 = 0//s2计数
let s2p = 0//s2指针
//当s1的数量没有超过总个数,就可以继续读取s1
while (counts1 < n1) {
//遍历s1中每一个字符
for (let i = 0; i < s1.length; i++) {
//如果相等指针后移
if (s1[i] === s2[s2p])
s2p++
//如果到最后一个,指针指回开头,s2计数加一
if (s2p === s2.length) {
counts2++
s2p = 0
}
}
// 用了一个s1,计数
counts1++
//如果s2的指针指到了开头,说明正好找到了循环的点
// 是否循环
if (s2p === 0) {
// 此时count1和count2就是循环了
let times = Math.floor(n1 / counts1)
counts1 *= times
counts2 *= times
//这里计数乘循环的次数,然后继续循环,因为counts1还可能是小于n1的,就是不能整除的时候
}
}
//返回结果。
return Math.floor(counts2 / n2)
};