前端算法第一九六弹-统计重复个数

77 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

定义 str = [s, n] 表示 strn 个字符串 s 连接构成。

  • 例如,str == ["abc", 3] =="abcabcabc"

如果可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。

  • 例如,根据定义,s1 = "abc" 可以从 s2 = "ab***dbe***c" 获得,仅需要删除加粗且用斜体标识的字符。

现在给你两个字符串 s1s2 和两个整数 n1n2 。由此构造得到两个字符串,其中 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

找出循环节

由于题目中的 n1n2 都很大,因此我们无法真正把 S1 = [s1, n1]S2 = [s2, n2] 都显式地表示出来。由于这两个字符串都是不断循环的,因此我们可以考虑找出 S2S1 中出现的循环节,如果我们找到了循环节,那么我们就可以很快算出 S2S1 中出现了多少次了。

算法

我们设计一个哈希表 recall :哈希表 recalls2 字符串的下标 index 为索引,存储匹配至第 s1cnts1 的末尾,当前匹配到第 s2cnts2 中的第 index 个字符时, 已经匹配过的s1 的个数 s1cnts2 的个数 s2cnt

我们在每次遍历至 s1 的末尾时根据当前匹配到的 s2 中的位置 index 查看哈希表中的对应位置,如果哈希表中对应的位置 index 已经存储元素,则说明我们找到了循环节。循环节的长度可以用当前已经匹配的 s1s2 的数量减去上次出现时经过的数量(即哈希表中存储的值)来得到。

然后我们就可以通过简单的运算求出所有构成循环节的 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)
};