<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?LeetCode刷題實戰(zhàn)76:最小覆蓋子串

          共 2636字,需瀏覽 6分鐘

           ·

          2020-10-25 02:29

          算法的重要性,我就不多說了吧,想去大廠,就必須要經過基礎知識和業(yè)務邏輯面試+算法面試。所以,為了提高大家的算法能力,這個公眾號后續(xù)每天帶大家做一道算法題,題目就從LeetCode上面選 !

          今天和大家聊的問題叫做?最小覆蓋子串,我們先來看題面:

          https://leetcode-cn.com/problems/minimum-window-substring/

          Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

          題意

          給你一個字符串 S、一個字符串 T 。請你設計一種算法,可以在 O(n) 的時間復雜度內,從字符串 S 里面找出:包含 T 所有字符的最小子串。

          樣例


          輸入:S = "ADOBECODEBANC", T = "ABC"
          輸出:"BANC"

          提示:

          如果 S 中不存這樣的子串,則返回空字符串 ""
          如果 S 中存在這樣的子串,我們保證它是唯一的答案。


          解題

          https://www.cnblogs.com/techflow/p/13082977.html

          分析

          我們來分析一下這個問題,從題意當中大家應該都能感受到它的難度。因為上來題目當中就限定了我們使用的算法的復雜度必須是,然而我們遍歷字符串的復雜度就已經是了,也就是說我們不能引入額外的計算開銷,否則一定不滿足題目的要求。
          可能有些同學會想到傳說中在時間內判斷字符串匹配的KMP算法,如果你不知道這個算法也沒有關系,因為這個算法并不適用。因為我們要找的不是完全相等的子串的位置,而是找的是字符構成一樣的子串,所以并不能通過引入字符串匹配算法來解決。沒有學過KMP算法的同學可以松一口氣了,這題當中并不會引入新的算法。

          解題的套路

          一般來說當我們面臨一個算法問題的時候,我們常常的思考過程主要有兩種。一種是適配,說白了就是把可能可以用上的算法往問題上套。根據(jù)題意先感覺一下,大概會用到什么樣的算法,然后詳細地推導適配的過程,看看是不是真的適用或者是有什么坑,或者是會出現(xiàn)什么新的問題。如果一切OK,能夠推理得通,那么這個算法就是解。第二種方法是建模,也就是說從題意入手,對題意進行深入的分析,對問題進行建模和抽象,找到問題的核心,從而推導出用什么樣的算法可以解決。
          舉個很簡單的例子,一般來說我們的動態(tài)規(guī)劃算法都是適配。都是我們先感覺或者是猜測出可以使用動態(tài)規(guī)劃,然后再去找狀態(tài)和轉移,最后建立狀態(tài)轉移方程。而一些搜索問題一般是建模,我們先對問題進行分析,然后找出需要搜索的解的存在空間,然后設計算法去搜索和剪枝,最后找到答案。
          據(jù)說一些頂級高手這兩種方法是一起使用的,所以才可以那么快速地找到解。當然我不是頂級高手,所以這個也只是我的猜測。這個思考過程非常有用,特別是當我們面試的時候,遇到一個從未見過的問題,如果你什么套路也沒有,頭腦一片空白或者是苦思冥想不得要領是很常見的事情。當你有了套路之后,你就可以試著慢慢找到答案了。
          回到這道題本身,我們剛才已經試過了,拿字符串匹配的算法網上套是不行的。在視野里似乎也沒有其他的算法可以套用,所以我們換一種思路,試試看建模。
          首先我們可以肯定一點,我們需要在遍歷的時候找到答案,這樣才可以保證算法的復雜度是。我們的目標是尋找子串,也就是說我們遍歷的過程應該對應一個子串,并且我們有方法可以快速判斷這個子串是否合法。這樣我們才可以做到遍歷的同時判斷答案的可行性。進而可以想到這是一個區(qū)間維護的問題,區(qū)間維護我們經常使用的方法就是two pointers。所以我們可以試試two pointers能否適用。
          實際上這道題的正解就是two pointers。

          題解

          我們維護了一個區(qū)間,我們需要判斷區(qū)間里的字符構成,這個很容易想到可以使用dict,維護每一個字符出現(xiàn)的次數(shù)。在這個題目當中,我們只需要考慮覆蓋的情況,也就是說字符多了并不會構成非法。所以我們可以維護一個dict,每次讀入一個字符更新它,當dict當中的字符滿足要求的時候,為了使得區(qū)間長度盡量短,我們可以試著移動區(qū)間的左側,盡量縮短區(qū)間的長度。
          從區(qū)間維護的角度來說,我們每次移動區(qū)間右側一個單位,只有當區(qū)間內已經滿足題意的時候才會移動左側。通過移動左側彈出元素來獲取能夠滿足題意的最佳區(qū)間。
          我們來看下主要的流程代碼:

          # 存儲區(qū)間內的字符
          segement = {}
          for?i in?range(n):
          ????segement[s[i]] += 1
          ????# 當滿足條件的時候移動區(qū)間左側
          ????while?l <= i and?satisified(segment):
          ????????# 更新最佳答案
          ????????if?i - l + 1?< ans_len:
          ????????????ans_len = i - l + 1
          ????????????beg, end?= l, i + 1
          ????????# 彈出元素
          ??segement[s[l]] -= 1
          ????????l += 1


          到這里還有一個小問題,就是怎么樣判斷這個segment是否合法呢?我們可以用一個數(shù)字matched來記錄目前已經匹配上的字符的數(shù)量。當某個字符在segment當中出現(xiàn)的次數(shù)和T中的次數(shù)相等的時候,matched加一。當matched的數(shù)量和T中字符種類的數(shù)量相等的時候,就可以認為已經合法了。
          我們把所有的邏輯串起來,就可以通過這題了。

          class?Solution:
          ????def?minWindow(self, s: str, t: str)?-> str:
          ????????from?collections import?Counter, defaultdict
          ????????# 通過Counter直接獲取T當中的字符構成
          ????????counter = Counter(t)
          ????????n, m = len(s), len(counter)
          ????????l, beg, end = 0, 0, 0
          ????????cur = defaultdict(int)
          ????????matched = 0
          ????????flag = False
          ????????# 記錄合法的字符串的長度
          ????????ans_len = 0x3f3f3f3f
          ????????
          ????????for?i in?range(n):
          ????????????if?s[i] not?in?counter:
          ????????????????continue
          ????????????????
          ????????????cur[s[i]] += 1
          ????????????# 當數(shù)量匹配上的時候,matched+1
          ????????????if?cur[s[i]] == counter[s[i]]:
          ????????????????matched += 1
          ????????????????
          ????????????# 如果已經找到了合法的區(qū)間,嘗試縮短區(qū)間的長度
          ????????????while?l <= i and?matched == m:
          ????????????????if?i - l + 1?< ans_len:
          ????????????????????flag = True
          ????????????????????beg, end = l, i+1
          ????????????????????ans_len = i - l + 1
          ????????????????????
          ????????????????# 彈出左側元素
          ????????????????c = s[l]
          ????????????????if?c in?counter:
          ????????????????????cur[c] -= 1
          ????????????????????if?cur[c] < counter[c]:
          ????????????????????????matched -= 1
          ????????????????????????
          ????????????????l += 1

          ????????
          ????????return?""?if?not?flag else?s[beg: end]


          總結

          到這里,這道題就算是解決了。很多同學可能會覺得疑惑,為什么我們用到了兩重循環(huán),但是它依然還是的算法呢?
          這個是two pointers算法的常見問題,也是老生常談的話題了。我們在分析復雜度的時候,不能只簡單地看用到了幾層循環(huán),而是要抓住計算的核心。比如在這個問題當中,我們內部的while循環(huán)針對的變量是l,l這個變量對于i整體是遞增的。也就是說無論外面這個循環(huán)執(zhí)行多少次,里面的這個while循環(huán)一共最多累加只能執(zhí)行n次。那么,當然這是一個的算法。
          好了,今天的文章就到這里,如果覺得有所收獲,請順手點個在看或者轉發(fā)吧,你們的支持是我最大的動力。


          上期推文:

          LeetCode40-60題匯總,速度收藏!
          LeetCode刷題實戰(zhàn)61:旋轉鏈表
          LeetCode刷題實戰(zhàn)62:不同路徑
          LeetCode刷題實戰(zhàn)63:不同路徑 II
          LeetCode刷題實戰(zhàn)64:最小路徑和
          LeetCode刷題實戰(zhàn)66:加一
          LeetCode刷題實戰(zhàn)67:二進制求和
          LeetCode刷題實戰(zhàn)68:文本左右對齊
          LeetCode刷題實戰(zhàn)69:x 的平方根
          LeetCode刷題實戰(zhàn)70:爬樓梯
          LeetCode刷題實戰(zhàn)71:簡化路徑
          LeetCode刷題實戰(zhàn)72:編輯距離
          LeetCode刷題實戰(zhàn)73:矩陣置零
          LeetCode刷題實戰(zhàn)74:搜索二維矩陣
          LeetCode刷題實戰(zhàn)75:顏色分類

          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品国产AⅤ一区二区三区东京热 | 高清无码视频在线免费看 | 久久成人激情 | 无码啪啪啪 | 国产无码影视 |