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。

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

相关推荐
魔道不误砍柴功1 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉1 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v1 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge1 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@1 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄1 小时前
SpringBoot
java·spring
数据小小爬虫1 小时前
如何用Java爬虫“偷窥”淘宝商品类目API的返回值
java·爬虫·php
暮春二十四1 小时前
关于用postman调用接口成功但是使用Java代码调用却失败的问题
java·测试工具·postman
java小吕布2 小时前
Java中Properties的使用详解
java·开发语言·后端
爱吃土豆的程序员2 小时前
在oracle官网下载资源显示400 Bad Request Request Header Or Cookie Too Large 解决办法
java·数据库·oracle·cookie