395: Longest Substring with At Least K Repeating Characters

题目链接:395

解题思路

对于substring、subarray这种求子集类的问题,常用的做法有两种:一是递归,二是滑动窗口。

递归天生就适合解决这类问题,因为它就是通过解决“子集”从而来解决“父集”的。

滑动窗口则是由于窗口内的所有元素其实就是一个“子集”,只不过通过某种具有“单调性”的策略将策略范围内的子集遍历了一遍,从而找到答案。滑动窗口相比于递归拥有更好的时间、空间复杂度,但它的使用限制则比递归多,只能解决具有“单调性”策略的问题。

根据官方题解,似乎可以用滑动窗口解决,但是我没有理解具体的做法。

而使用递归解决问题的时候,最重要的是回答三个问题:

  1. 递归函数的物理意义是什么?
  2. 递归的终止条件是什么?
  3. 对于当前层来说,子问题是什么?

对于这道题,也可以通过回答这三个问题来明确递归思路。

  1. 设计的递归函数 longestSubstring(s string, k int) int,它的物理意义即题意:给定一个字符串s,找到长度最长的满足条件的子字符串,该子字符串的每一种字符都出现了k次以上。
  2. 终止条件是:1. s 的长度小于 k,不存在符合条件的子串,返回0;2. 当前 s 即满足条件,即 s 中每个字符都出现 k 次以上,返回 s 的长度。
  3. 将当前字符串根据不符合条件的字符进行分割,分割后的不包含非法字符的子串即为新的子问题,而当前问题的答案即为所有子问题的答案的最大值。

根据以上分析,可以写出代码:

// 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)

你可能感兴趣的:(395: Longest Substring with At Least K Repeating Characters)