LeetCode 466 统计重复个数


文章目录

摘要

《统计重复个数》是一道看起来像字符串题,实际上是模式发现 + 数学加速的题

很多人第一次写这道题,都会下意识用"模拟拼字符串"的方式,结果很快就会发现:
字符串根本拼不动,n1n2 最大是 10⁶,暴力就是在作死。

这道题真正考的不是字符串操作,而是:

  • 如何把"重复结构"找出来
  • 如何用一次循环,干掉成千上万次重复计算

如果你平时做过日志分析、消息消费、批量规则匹配,这道题的思路会非常熟。

描述

题目里定义了一个挺绕的概念:

复制代码
str = [s, n]  表示 s 重复 n 次拼接

比如:

复制代码
[s1, n1] = ["acb", 4] => "acbacbacbacb"

然后又给了一个规则:

如果可以从 s2 中删除一些字符,得到 s1,那么就说 s1 可以从 s2 获得。

注意这里是子序列,不是子串,字符顺序要对,但可以跳着删。

最终问题是:

str1 = [s1, n1] 中,最多能找出多少个完整的
str2 = [s2, n2]

换句话说:
s1 重复 n1 次,最多能"拼"出多少组 s2 重复 n2 次。

题解答案

这道题的核心思路只有一句话:

暴力模拟一次 s1,记录匹配 s2 的状态,一旦发现循环,就直接数学跳跃。

整体拆解成三步:

  1. 模拟 s1 的字符流,去匹配 s2
  2. 记录"每次 s1 结束时,s2 匹配到了哪里"
  3. 一旦发现状态重复,说明进入循环,可以直接计算答案

这是一个非常经典的 状态压缩 + 循环检测 的套路。

题解代码分析

下面是完整 Swift 实现,可以直接在 LeetCode 或本地 Playground 运行。

swift 复制代码
class Solution {
    func getMaxRepetitions(_ s1: String, _ n1: Int, _ s2: String, _ n2: Int) -> Int {
        let s1Arr = Array(s1)
        let s2Arr = Array(s2)

        let len1 = s1Arr.count
        let len2 = s2Arr.count

        // indexRecorder[i] 表示:第 i 次 s1 用完后,s2 当前匹配到的位置
        // countRecorder[i] 表示:第 i 次 s1 用完后,总共匹配了多少个 s2
        var indexRecorder = [Int](repeating: 0, count: n1 + 1)
        var countRecorder = [Int](repeating: 0, count: n1 + 1)

        var index2 = 0
        var count2 = 0

        for i in 1...n1 {
            for c in s1Arr {
                if c == s2Arr[index2] {
                    index2 += 1
                    if index2 == len2 {
                        index2 = 0
                        count2 += 1
                    }
                }
            }

            indexRecorder[i] = index2
            countRecorder[i] = count2

            // 检查是否出现循环
            for k in 0..<i {
                if indexRecorder[k] == index2 {
                    // 找到循环
                    let preCount = countRecorder[k]
                    let loopCount = countRecorder[i] - countRecorder[k]
                    let loopLength = i - k

                    let remaining = n1 - k
                    let loops = remaining / loopLength
                    let rest = remaining % loopLength

                    let total = preCount
                        + loops * loopCount
                        + (countRecorder[k + rest] - countRecorder[k])

                    return total / n2
                }
            }
        }

        return countRecorder[n1] / n2
    }
}

关键逻辑拆解

为什么要记录 index2

index2 表示:
当前 s2 已经匹配到了第几个字符

如果某一次 s1 用完后,index2 和之前某次完全一样,那说明:

后面的匹配过程会一模一样

进入了"死循环"

这就和我们在实际系统里发现"消费 offset 重复"是一个道理。

为什么可以直接数学计算?

一旦进入循环:

  • 每一轮循环,s1 消耗固定次数
  • 每一轮循环,s2 增加固定个数

那剩下的就不需要一轮一轮算了,直接:

复制代码
循环次数 × 每轮收益
这一步为什么这么重要?
swift 复制代码
return total / n2

因为题目问的是:

能拼出多少个完整的 [s2, n2]

不是 s2 的次数,而是 多少组 n2

示例测试及结果

示例 1

swift 复制代码
let solution = Solution()
print(solution.getMaxRepetitions("acb", 4, "ab", 2))
推演一下

s1 = "acb"
s2 = "ab"

  • 每一轮 s1,都能匹配出一个 "ab"
  • n1 = 4,一共能得到 4 个 "ab"
  • 2"ab" 才算一组

最终结果:

复制代码
2

示例 2

swift 复制代码
print(solution.getMaxRepetitions("acb", 1, "acb", 1))

s1s2 完全一样,一次就够。

输出:

复制代码
1

时间复杂度

  • 外层最多跑 n1
  • 每次扫描 s1 的长度(最大 100)
  • 循环检测最多 n1

在最坏情况下是:

复制代码
O(n1 * (|s1| + n1))

但实际上由于 循环很早就会出现,性能远好于理论最坏值。

空间复杂度

主要使用了两个数组:

  • indexRecorder
  • countRecorder

空间复杂度为:

复制代码
O(n1)

在题目限制内完全可控。

总结

《统计重复个数》是一道非常值得反复琢磨的题,它教会你的不是字符串技巧,而是:

  • 如何识别"重复状态"
  • 如何把线性模拟升级成"数学跳跃"
  • 如何避免无意义的重复计算
相关推荐
AI爱好者202012 小时前
智能优化算法2025年新书推荐——《智能优化算法及其MATLAB实例(第4版)》
开发语言·算法·matlab
LYFlied13 小时前
【每日算法】LeetCode215. 数组中的第K个最大元素
前端·算法
2501_9418227513 小时前
从限流降载到全链路流控的互联网工程语法实践与多语言探索
leetcode·模拟退火算法
炽烈小老头13 小时前
【每天学习一点算法 2026/01/06】最小栈
学习·算法·leetcode
mit6.82413 小时前
hadoop|贪心
算法
2501_9418053113 小时前
在阿姆斯特丹智能港口场景中构建集装箱实时调度与高并发物流数据分析平台的工程设计实践经验分享
java·大数据·算法
涂山小楼13 小时前
线程join()方法的深度理解
java·前端·算法
六毛的毛13 小时前
填充每个节点的下一个右侧节点指针
leetcode
gihigo199813 小时前
LDPC码硬判决译码算法的详细解析
网络·算法
Clarence Liu13 小时前
快慢指针问题
后端·算法