LeetCode题练习与总结:至少有 K 个重复字符的最长子串--395

一、题目描述

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

如果不存在这样的子字符串,则返回 0。

示例 1:

复制代码
输入:s = "aaabb", k = 3
输出:3
解释:最长子串为 "aaa" ,其中 'a' 重复了 3 次。

示例 2:

复制代码
输入:s = "ababbc", k = 2
输出:5
解释:最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。

提示:

  • 1 <= s.length <= 10^4
  • s 仅由小写英文字母组成
  • 1 <= k <= 10^5

二、解题思路

这个问题可以使用分治法的思想来解决。首先,我们可以遍历字符串,统计每个字符的出现次数。然后,找出那些出现次数少于k的字符,因为这些字符不能出现在结果子串中。我们将字符串根据这些字符分割成多个子串,然后递归地在每个子串中寻找满足条件的最长子串。

具体步骤如下:

  1. 统计字符串中每个字符的出现次数。
  2. 找出所有出现次数少于k的字符。
  3. 根据这些字符将原字符串分割成多个子串。
  4. 对每个子串递归地执行上述步骤,直到找到满足条件的最长子串。

三、具体代码

java 复制代码
class Solution {
    public int longestSubstring(String s, int k) {
        // 如果字符串为空或者长度小于k,直接返回0
        if (s == null || s.length() < k) return 0;
        
        // 统计每个字符的出现次数
        int[] counts = new int[26];
        for (char c : s.toCharArray()) {
            counts[c - 'a']++;
        }
        
        // 遍历每个字符,找出出现次数少于k的字符
        for (int i = 0; i < 26; i++) {
            if (counts[i] > 0 && counts[i] < k) {
                // 找到出现次数少于k的字符,分割字符串
                int res = 0;
                for (String str : s.split(String.valueOf((char)(i + 'a')))) {
                    // 递归处理每个子串
                    res = Math.max(res, longestSubstring(str, k));
                }
                return res;
            }
        }
        
        // 如果所有字符的出现次数都不少于k,返回原字符串长度
        return s.length();
    }
}

这段代码首先统计了字符串中每个字符的出现次数,然后检查哪些字符的出现次数少于k。对于每个这样的字符,它将字符串分割成多个子串,并对每个子串递归地调用longestSubstring函数。最终,它会返回找到的最长子串的长度。如果字符串中的所有字符都至少出现了k次,那么它会直接返回字符串的长度。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 统计每个字符的出现次数:这个步骤需要遍历字符串一次,所以时间复杂度是O(n),其中n是字符串的长度。

  • 遍历字符统计数组:这个步骤需要遍历一个固定大小(26)的数组,所以时间复杂度是O(1)。

  • 分割字符串:在最坏的情况下,如果字符串中的每个字符都至少出现了一次,那么我们需要分割n个子串(每个字符对应一个子串),每个子串的分割操作是O(n),所以这部分的时间复杂度是O(n^2)。

  • 递归处理子串:对于每个子串,我们都需要调用longestSubstring函数。在最坏的情况下,每次递归调用都会将字符串分割成两个部分,所以递归的深度是O(log n)。每个递归调用的处理时间是O(n),所以递归部分的总时间复杂度是O(n log n)。

综合以上步骤,我们可以得出总的时间复杂度是O(n^2) + O(n log n),由于O(n^2)是主导项,所以总的时间复杂度是O(n^2)。

2. 空间复杂度
  • 字符统计数组:这个数组的大小是固定的,为26,所以空间复杂度是O(1)。

  • 递归栈空间:在最坏的情况下,递归的深度是O(log n),所以递归栈的空间复杂度是O(log n)。

  • 分割字符串:在分割字符串时,会创建新的字符串对象,这些对象的空间复杂度是O(n)。

综合以上步骤,我们可以得出总的空间复杂度是O(n) + O(log n),由于O(n)是主导项,所以总的空间复杂度是O(n)。

五、总结知识点

  • 基础编程概念:

    • if 语句:用于条件判断。
    • for 循环:用于遍历集合或重复执行代码块。
    • return 语句:用于从方法中返回值。
  • 字符串操作:

    • toCharArray():将字符串转换为字符数组。
    • split(String regex):根据给定的正则表达式分割字符串,并返回分割后的字符串数组。
  • 数组:

    • 声明和初始化数组:int[] counts = new int[26];。
    • 访问数组元素:counts[c - 'a']++;。
  • 字符与ASCII码:

    • 字符与整数之间的转换:通过'a'来计算字符在数组中的索引。
  • 递归:

    • 方法自身调用自身:longestSubstring(str, k)。
  • 字符串分割与递归结合:

    • 使用split方法将字符串分割成多个子串,并对每个子串递归调用相同的逻辑。
  • 数学运算:

    • Math.max(int a, int b):返回两个整数中的最大值。
  • 算法设计:

    • 分治法:将大问题分解为小问题,递归地解决小问题,然后合并结果。
  • 边界条件处理:

    • 检查字符串是否为空或者长度是否小于k,并返回0。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

相关推荐
智鸟科技GemeOpen开发者智能设备3 分钟前
GemeOpen 智能音箱 GSSM0B - 播放控制(Java示例)
java·开发语言·智能音箱
需要82612 分钟前
分布式 ID 生成:雪花算法、号段模式与时钟回拨
分布式·算法
垆边人似月.19 分钟前
华为机考题:字符串分隔
算法·华为
今天的砖头有点烫手啊21 分钟前
Java IO/NIO/AIO 演进:从 BIO 到 Netty 的底层逻辑
java·jvm·nio
CQU_JIAKE22 分钟前
9.4【A】
算法
CQU_JIAKE26 分钟前
9.13[A]
算法
懿路向前36 分钟前
【唤醒实战笔记】2026-09-29 | 智能体提示词迭代史(v1 → v8)
linux·笔记·算法
谢亮_vipxieliang1 小时前
Java 21 新特性实战:Record、Sealed、模式匹配
java·开发语言
@陈小鱼1 小时前
基于CNN-Transformer的无袖带血压估计
人工智能·深度学习·神经网络·算法·cnn·transformer·血压
霸道流氓气质1 小时前
LLM 应用限流与熔断机制完全指南:从多层防护架构到Java生产级弹性实战
java·开发语言·架构