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。

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

相关推荐
graceyun16 分钟前
C语言进阶习题【1】指针和数组(4)——指针笔试题3
android·java·c语言
我科绝伦(Huanhuan Zhou)20 分钟前
Linux 系统服务开机自启动指导手册
java·linux·服务器
旦沐已成舟1 小时前
K8S-Pod的环境变量,重启策略,数据持久化,资源限制
java·docker·kubernetes
S-X-S1 小时前
项目集成ELK
java·开发语言·elk
Ting-yu1 小时前
项目实战--网页五子棋(游戏大厅)(3)
java·java-ee·maven·intellij-idea
小孟Java攻城狮4 小时前
leetcode-不同路径问题
算法·leetcode·职场和发展
查理零世4 小时前
算法竞赛之差分进阶——等差数列差分 python
python·算法·差分
程序研6 小时前
JAVA之外观模式
java·设计模式
计算机学姐6 小时前
基于微信小程序的驾校预约小程序
java·vue.js·spring boot·后端·spring·微信小程序·小程序
黄名富6 小时前
Kafka 日志存储 — 日志索引
java·分布式·微服务·kafka