【分治】力扣395:至少有 K 个重复字符的最长子串
给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。
提示:s 仅由小写英文字母组成
示例:
输入:s = "ababbc", k = 2
输出:5
解释:最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。
求最长子字符串/区间的这类题一般可以用滑动窗口来做,但是本题滑动窗口的代码不好写,改用递归。
重点:在调用递归函数的时候,把递归函数当做普通函数(黑箱)来调用,即明白该函数的输入输出是什么,而不用管此函数内部在做什么。
- 递归最基本的是记住递归函数的含义:本题的 longestSubstring(s, k) 函数表示的就是题意,即求一个最长的子字符串的长度,该子字符串中每个字符出现的次数都最少为 k。函数入参 s 是表示源字符串;k 是限制条件,即子字符串中每个字符最少出现的次数;函数返回结果是满足题意的最长子字符串长度。
2.递归的终止条件(能直接写出的最简单 case):如果字符串 s 的长度少于 k,那么一定不存在满足题意的子字符串,返回 0; - 调用递归(重点):如果一个字符 ch 在 s 中出现的次数少于 k 次,那么 s 中所有的包含 ch 的子字符串都不能满足题意。所以,应该在 s 的所有不包含 ch 的子字符串中继续寻找结果:
- 把 s 按照 ch 分割(分割后每个子串都不包含 ch),得到很多子字符串 tt
- 要求t 作为源字符串的时候,它的最长的满足题意的子字符串长度(到现在为止,把大问题分割为了小问题(s → t))。此时发现,恰好已经定义了函数 longestSubstring(s, k) 就是来解决这个问题的!所以直接把 longestSubstring(s, k) 函数拿来用,于是形成了递归。
- 未进入递归时的返回结果:如果 s 中的每个字符出现的次数都大于 k 次,那么 s 就是我们要求的字符串,直接返回该字符串的长度。
总之,通过上面的分析看出:不是为了递归而递归。而是因为把大问题拆解成了小问题,恰好有函数可以解决小问题,所以直接用这个函数。由于这个函数正好是本身,所以我们把此现象叫做递归。
小问题是原因,递归是结果。而递归函数到底怎么一层层展开与终止的,不要用大脑去想,这是计算机干的事。我们只用把递归函数当做一个能解决问题的黑箱就够了,把更多的注意力放在拆解子问题、递归终止条件、递归函数的正确性上来。
作者:fuxuemingzhu
链接:https://leetcode-cn.com/problems/longest-substring-with-at-least-k-repeating-characters/solution/jie-ben-ti-bang-zhu-da-jia-li-jie-di-gui-obla/
class Solution:
def longestSubstring(self, s: str, k: int) -> int:
if len(s) < k:
return 0
for c in set(s):
if s.count(c) < k:
return max(self.longestSubstring(t, k) for t in s.split(c))
return len(s)
时间复杂度:O(N * 26 * 26),因为函数最多执行 26 次,for循环遍历一次是26个字符,循环里面对 s 分割时间复杂度是O(N)。
空间复杂度:O(26 * 26),函数执行 26 次,每次开辟 26 个字符的set空间。

浙公网安备 33010602011771号