题目链接:395
解题思路
对于substring、subarray这种求子集类的问题,常用的做法有两种:一是递归,二是滑动窗口。
递归天生就适合解决这类问题,因为它就是通过解决“子集”从而来解决“父集”的。
滑动窗口则是由于窗口内的所有元素其实就是一个“子集”,只不过通过某种具有“单调性”的策略将策略范围内的子集遍历了一遍,从而找到答案。滑动窗口相比于递归拥有更好的时间、空间复杂度,但它的使用限制则比递归多,只能解决具有“单调性”策略的问题。
根据官方题解,似乎可以用滑动窗口解决,但是我没有理解具体的做法。
而使用递归解决问题的时候,最重要的是回答三个问题:
- 递归函数的物理意义是什么?
- 递归的终止条件是什么?
- 对于当前层来说,子问题是什么?
对于这道题,也可以通过回答这三个问题来明确递归思路。
- 设计的递归函数 longestSubstring(s string, k int) int,它的物理意义即题意:给定一个字符串s,找到长度最长的满足条件的子字符串,该子字符串的每一种字符都出现了k次以上。
- 终止条件是:1. s 的长度小于 k,不存在符合条件的子串,返回0;2. 当前 s 即满足条件,即 s 中每个字符都出现 k 次以上,返回 s 的长度。
- 将当前字符串根据不符合条件的字符进行分割,分割后的不包含非法字符的子串即为新的子问题,而当前问题的答案即为所有子问题的答案的最大值。
根据以上分析,可以写出代码:
// use recursion
// recursion rule: give a string s, return the longest substring's length that is satisfed
func longestSubstring(s string, k int) int {
// Base case
if len(s) < k {
return 0
}
// scan the string, use two maps to record info
// m1: key: char; val: appearing times
// valid: record satisfied character, key: char; val: bool
m1 := make(map[byte]int)
valid := make(map[byte]bool)
for i := range s {
ch := s[i]
count, ok := m1[ch]
if !ok {
m1[ch] = 1
} else {
m1[ch] = count + 1
}
// 满足条件了,加入 valid map 中
if m1[ch] >= k {
valid[ch] = true
}
}
// all characters are satisfied
if len(m1) == len(valid) {
return len(s)
}
// use sliding window to discard invalid character and split the string
// all characters in substring between [left, right) are valid, do dfs on this substring
left := 0
right := 0
maxLength := 0
for right < len(s) {
// find left bound
for left < len(s) && !valid[s[left]] {
left++
}
// refresh right bound
if right < left {
right = left
}
// find right bound
for right < len(s) && valid[s[right]] {
right++
}
// record ans
length := longestSubstring(s[left:right], k)
maxLength = max(maxLength, length)
// refresh left bound
left = right
}
return maxLength
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
时间复杂度:O(N * 26)。N为字符串的长度,26为字符集的个数,本题中字符为所有小写字母,即26个。由于每一次递归都能完全去除一种字符(即一种小写字母),因此递归最深有26层,每一层最多遍历N个元素,因此时间复杂度是O(N * 26)
空间复杂度:O(26 * 26)。由于最深能够递归26层,每一层又开辟了map,该map最多存26个键值对,即一个map O(26)的空间复杂度,因此总共的空间复杂度是 O(26 * 26)