当前位置: 首页 > news >正文

LeetCode 395 - 至少有 K 个重复字符的最长子串

在这里插入图片描述
在这里插入图片描述

文章目录

    • 摘要
    • 描述
      • 示例 1:
      • 示例 2:
    • 题解答案
    • 题解代码分析
      • 代码解析
        • **1. 统计字符出现次数**
        • 2. 找出不合格的字符
        • 3. 分治拆分 + 递归求解
        • 4. 所有字符都合格
    • 示例测试及结果
      • 输出结果:
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

在日常开发中,我们经常需要分析字符串,比如日志过滤、关键字提取、或者做文本分析。这类问题的核心都是“在一个字符串中找出满足某种约束的最长片段”。

LeetCode 第 395 题 “至少有 K 个重复字符的最长子串” 就是这种典型问题之一。它考察了我们对分治思想的理解,以及如何结合递归与频次统计快速定位符合条件的子串。

虽然看起来像是一个“滑动窗口”问题,但其实这题最优解并不是窗口遍历,而是一个非常有意思的 “分而治之” 过程。

描述

题目要求:
给定一个字符串 s 和一个整数 k,我们需要找到 s 中的最长子串,这个子串的每一个字符都至少重复了 k 次。

简单来说,就是:

所有字符的出现次数都要 ≥ k。

如果找不到符合要求的子串,返回 0。

示例 1:

输入:s = "aaabb", k = 3
输出:3
解释:最长子串为 "aaa"

示例 2:

输入:s = "ababbc", k = 2
输出:5
解释:最长子串为 "ababb"

题解答案

这个问题的关键在于:

哪些字符“不够格”(出现次数 < k),它们就不能出现在最终的结果中。

因为任何包含这些字符的子串都不可能满足条件。
所以可以把这些“不合格的字符”作为分割点,把原字符串拆成若干子串,然后递归地在每个子串中寻找最长合法部分。

这其实是一种 “分治法(Divide and Conquer)” 的思路。

题解代码分析

下面是完整的 Swift 实现,逻辑清晰,可直接运行

import Foundationclass Solution {func longestSubstring(_ s: String, _ k: Int) -> Int {// 基础情况:如果字符串长度小于 k,肯定不可能有合法子串if s.count < k { return 0 }// 统计字符出现次数var freq: [Character: Int] = [:]for ch in s {freq[ch, default: 0] += 1}// 找出第一个不满足条件的字符for (ch, count) in freq {if count < k {// 用这个字符作为“分割点”let parts = s.split(separator: ch)// 对每个部分递归求解var maxLen = 0for part in parts {maxLen = max(maxLen, longestSubstring(String(part), k))}return maxLen}}// 如果所有字符都满足条件,那整个字符串就是结果return s.count}
}

代码解析

整个算法的思想可以分为三个阶段来看:

1. 统计字符出现次数

我们先遍历字符串,用字典(freq)记录每个字符出现的次数。

var freq: [Character: Int] = [:]
for ch in s {freq[ch, default: 0] += 1
}

如果字符串长度小于 k,那肯定直接返回 0,不用多想。

2. 找出不合格的字符

如果某个字符出现次数小于 k,它就是整个问题的“拦路虎”。
因为任何包含它的子串都不可能满足要求。

for (ch, count) in freq {if count < k {// 找到第一个不合格字符...}
}
3. 分治拆分 + 递归求解

一旦发现“不合格的字符”,就以它为分界线把字符串切开。

举个例子:

s = "aaabbcc", k = 3

这里 'b' 只出现了 2 次,不达标。
那么就以 'b' 为分割点,把字符串分成两部分:

  • "aaa"
  • "cc"

然后递归在每一部分中分别求解。
最后取这两部分结果的最大值。

这一段的核心逻辑是:

let parts = s.split(separator: ch)
var maxLen = 0
for part in parts {maxLen = max(maxLen, longestSubstring(String(part), k))
}
return maxLen
4. 所有字符都合格

如果遍历完所有字符后都没有发现“不合格的字符”,那说明整个字符串本身就是合法的,直接返回 s.count

return s.count

示例测试及结果

我们来测试几个案例

let solution = Solution()print(solution.longestSubstring("aaabb", 3))      // 输出:3
print(solution.longestSubstring("ababbc", 2))     // 输出:5
print(solution.longestSubstring("aabcabb", 2))    // 输出:4
print(solution.longestSubstring("abcd", 2))       // 输出:0

输出结果:

3
5
4
0

解释:

  • "aaabb""aaa" 是最长的合法子串;
  • "ababbc""ababb" 满足条件;
  • "aabcabb" → 最长合法子串是 "abba""bcabb"
  • "abcd" → 所有字符只出现一次,不满足 k=2

时间复杂度

这道题的复杂度分析比较有趣。

最坏情况下,每次递归都可能遍历整个字符串,因此:

  • 时间复杂度约为 O(26 * n),因为我们最多只会针对 26 个字母做拆分。

对一般情况来说,性能表现非常稳定。

空间复杂度

递归栈的深度最多为 26 层(因为每次可能由不合格的字符切分一次),
同时字典统计频次的空间是 O(26)。

所以整体空间复杂度是 O(26 + 递归深度)O(26),也就是常数级别。

总结

这道题的精髓就在于“分治”两个字。
我们不是去暴力枚举所有子串,而是通过统计信息快速确定哪些字符“拖后腿”,再把它们作为分界线递归处理剩余部分。

这种思路非常适合解决**“全局约束 + 局部分割”**类的问题,比如:

  • “最长有效括号”
  • “满足特定条件的连续区间”
  • “日志分片分析”

在实际开发中,如果你在做文本分析(比如统计出现频率、提取关键词等),这种方法也能派上用场。
比如在做“日志聚类”时,你也可以先找出低频字符做分割,从而减少计算量。

http://www.dtcms.com/a/469991.html

相关文章:

  • 科技有限公司可以做网站建设吗成都网站网络建设
  • Qt绘制折线图
  • Idea中新建package包,变成了Directory
  • 如何自建淘宝客网站wordpress 知笔墨
  • Python爬虫实战:腾讯控股2024年资产负债分析
  • AI-调查研究-100-具身智能 现代AI方法全解析:强化学习、模仿学习与Transformer在机器人控制中的应用
  • Docker核心技术:深入理解网络模式 ——Host/None/Container 模式与混合云原生架构实践
  • 南通市住房城乡建设局网站磁力蜘蛛种子搜索
  • 解决HTML塌陷的方法
  • sqlite 使用: 03-问题记录:在使用 sqlite3_bind_text 中设置 SQLITE_STATIC 参数时,处理不当造成的字符乱码
  • 网站建设与维护难不难为什么找别人做网站
  • 广州木马网站建设公司医院门户网站建设规划
  • 大模型学习之 深入理解编码器与解码器
  • pyqt 触摸屏监听
  • C++ Primer Plus 第六版 第十三章 编程题
  • 大模型前世今生(十二):Hessian矩阵
  • 蛙跳积分法:分子动力学模拟中的高效数值积分技术
  • 详解 SNMPv1 与 SNMPv2 Trap 格式
  • 书法网站建设成都微信公众号制作
  • 宜春网站制作公司wordpress图片上传慢
  • Python串口通信与MQTT物联网网关:连接STM32与物联网平台
  • MyLanViewer(局域网IP扫描软件)
  • 湛江专业建站推荐40平米小户型装修效果图
  • 147.《手写实现 Promise.all 与 Promise.race》
  • 【HarmonyOS】异步并发和多线程并发
  • 使用docker 安装dragonfly带配置文件(x86和arm)版本
  • 企业信息型网站有哪些网站建设塞西
  • 怎么看网站是什么程序做的益阳网络
  • SpringBoot通过配置类替换配置文件配置
  • 使用Customplot绘制时间-数据曲线