1. 程式人生 > 其它 >【分治】力扣395:至少有 K 個重複字元的最長子串

【分治】力扣395:至少有 K 個重複字元的最長子串

給你一個字串 s 和一個整數 k ,請你找出 s 中的最長子串, 要求該子串中的每一字元出現次數都不少於 k 。返回這一子串的長度。
提示:s 僅由小寫英文字母組成

示例:

輸入:s = "ababbc", k = 2
輸出:5
解釋:最長子串為 "ababb" ,其中 'a' 重複了 2 次, 'b' 重複了 3 次。

求最長子字串/區間的這類題一般可以用滑動視窗來做,但是本題滑動視窗的程式碼不好寫,改用遞迴。

重點:在呼叫遞迴函式的時候,把遞迴函式當做普通函式(黑箱)來呼叫,即明白該函式的輸入輸出是什麼,而不用管此函式內部在做什麼。

  1. 遞迴最基本的是記住遞迴函式的含義:本題的 longestSubstring(s, k) 函式表示的就是題意,即求一個最長的子字串的長度,該子字串中每個字元出現的次數都最少為 k。函式入參 s 是表示源字串;k 是限制條件,即子字串中每個字元最少出現的次數;函式返回結果是滿足題意的最長子字串長度。
    2.遞迴的終止條件(能直接寫出的最簡單 case):
    如果字串 s 的長度少於 k,那麼一定不存在滿足題意的子字串,返回 0;
  2. 呼叫遞迴(重點):如果一個字元 ch 在 s 中出現的次數少於 k 次,那麼 s 中所有的包含 ch 的子字串都不能滿足題意。所以,應該在 s 的所有不包含 ch 的子字串中繼續尋找結果:
    • 把 s 按照 ch 分割(分割後每個子串都不包含 ch),得到很多子字串 tt
    • 要求t 作為源字串的時候,它的最長的滿足題意的子字串長度(到現在為止,把大問題分割為了小問題(s → t))。此時發現,恰好已經定義了函式 longestSubstring(s, k) 就是來解決這個問題的!所以直接把 longestSubstring(s, k) 函式拿來用,於是形成了遞迴。
  3. 未進入遞迴時的返回結果:如果 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空間。