精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

滑動(dòng)窗口算法解決子串問題教程

jf_78858299 ? 來源:labuladong ? 作者:labuladong ? 2023-04-19 11:06 ? 次閱讀

現(xiàn)在寫滑動(dòng)窗口算法的時(shí)機(jī)應(yīng)該是合適的,因?yàn)樽x者已經(jīng)通過 [雙指針技巧匯總]理解了雙指針的套路,通過 [單調(diào)隊(duì)列解決滑動(dòng)窗口問題]對(duì)滑動(dòng)窗口這個(gè)東西有了個(gè)印象,而且通過 [一個(gè)方法團(tuán)滅股票問題] 看到了成體系方法舉一反三的威力。

本文詳解「滑動(dòng)窗口」這種高級(jí)雙指針技巧的算法框架,帶你秒殺幾道高難度的子字符串匹配問題。

LeetCode 上至少有 9 道題目可以用此方法高效解決。但是有幾道是 VIP 題目,有幾道題目雖不難但太復(fù)雜,所以本文只選擇點(diǎn)贊最高,較為經(jīng)典的,最能夠講明白的三道題來講解。第一題為了讓讀者掌握算法模板,篇幅相對(duì)長(zhǎng),后兩題就基本秒殺了。文章最后抽象出一個(gè)簡(jiǎn)單的算法框架。

本文代碼為 C++ 實(shí)現(xiàn),不會(huì)用到什么編程方面的奇技淫巧,但是還是簡(jiǎn)單介紹一下一些用到的數(shù)據(jù)結(jié)構(gòu),以免有的讀者因?yàn)檎Z言的細(xì)節(jié)問題阻礙對(duì)算法思想的理解:

unordered_map 就是哈希表(字典),它的一個(gè)方法 count(key) 相當(dāng)于 containsKey(key) 可以判斷鍵 key 是否存在。

可以使用方括號(hào)訪問鍵對(duì)應(yīng)的值 map[key]。需要注意的是,如果該 key 不存在,C++ 會(huì)自動(dòng)創(chuàng)建這個(gè) key,并把 map[key] 賦值為 0。

所以代碼中多次出現(xiàn)的 map[key]++ 相當(dāng)于 Java 的 map.put(key, map.getOrDefault(key, 0)+1)。

PS:本文大的主要代碼都是圖片形式,可以點(diǎn)開放大,更重要的是可以左右滑動(dòng)方便對(duì)比代碼。

一、最小覆蓋子串

圖片

題目不難理解,要求在串 S(source) 中找到包含串 T(target) 中全部字母的一個(gè)子串,順序無所謂,但這個(gè)子串得是所有可能子串中最短的。

此題難度 Hard,但是因?yàn)楹苡写硇裕苑诺降谝坏馈?/p>

如果我們使用暴力解法,代碼大概是這樣的:

for (int i = 0; i < s.size(); i++)
    for (int j = i + 1; j < s.size(); j++)
        if s[i:j] 包含 t 的所有字母:
            更新答案

思路很直接吧,但是顯然,這個(gè)算法的復(fù)雜度肯定大于 O(N^2) 了,不好。

滑動(dòng)窗口算法的思路是這樣:

1 、我們?cè)谧址?S 中使用雙指針中的左右指針技巧,初始化 left = right = 0,把索引閉區(qū)間 [left, right] 稱為一個(gè)「窗口」。

2 、我們先不斷地增加 right 指針擴(kuò)大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。

3 、此時(shí),我們停止增加 right,轉(zhuǎn)而不斷增加 left 指針縮小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同時(shí),每次增加 left,我們都要更新一輪結(jié)果。

4 、重復(fù)第 2 和第 3 步,直到 right 到達(dá)字符串 S 的盡頭。

這個(gè)思路其實(shí)也不難, 第 2 步相當(dāng)于在尋找一個(gè)「可行解」,然后第 3 步在優(yōu)化這個(gè)「可行解」,最終找到最優(yōu)解。 左右指針輪流前進(jìn),窗口大小增增減減,窗口不斷向右滑動(dòng)。

下面畫圖理解一下,needs 和 window 相當(dāng)于計(jì)數(shù)器,分別記錄 T 中字符出現(xiàn)次數(shù)和窗口中的相應(yīng)字符的出現(xiàn)次數(shù)。

初始狀態(tài):

圖片

增加 right,直到窗口 [left, right] 包含了 T 中所有字符:

圖片

現(xiàn)在開始增加 left,縮小窗口 [left, right](此時(shí)窗口里的字符串是最優(yōu)解):

圖片

直到窗口中的字符串不再符合要求,left 不再繼續(xù)移動(dòng):

圖片

之后重復(fù)上述過程,再移動(dòng) right 試圖使窗口中的字符再次符合要求,之后移動(dòng) left 縮小窗口… 直到 right 指針到達(dá)字符串 S 的末端,算法結(jié)束。

在算法執(zhí)行過程中,就可以找到長(zhǎng)度最短符合條件的子串(窗口)。

如果你能夠理解上述過程,恭喜,你已經(jīng)完全掌握了滑動(dòng)窗口算法思想。至于如何具體到問題,如何得出此題的答案,都是編程問題,很容易解決。

上述過程可以簡(jiǎn)單地寫出如下偽碼框架:

string s, t;
// 在 s 中尋找 t 的「最小覆蓋子串」
int left = 0, right = 0;
string res = s;
// 先移動(dòng) right 尋找可行解
while(right < s.size()) {
    window.add(s[right]);
    right++;
    // 找到可行解后,開始移動(dòng) left 縮小窗口
    while (window 符合要求) {
        // 如果這個(gè)窗口的子串更短,則更新結(jié)果
        res = minLen(res, window);
        window.remove(s[left]);
        left++;
    }
}
return res;

如果上述代碼你也能夠理解,那么你離解題更近了一步。現(xiàn)在就剩下一個(gè)比較棘手的問題:

如何判斷 window 即子串 s[left...right] 是否符合要求,即是否包含 t 的所有字符呢?

可以用兩個(gè)哈希表當(dāng)作計(jì)數(shù)器解決。用一個(gè)哈希表 needs 記錄字符串 t 中包含的字符及出現(xiàn)次數(shù),用另一個(gè)哈希表 window 記錄當(dāng)前「窗口」中包含的字符及出現(xiàn)的次數(shù)。如果 window 包含所有 needs 中的鍵,且這些鍵對(duì)應(yīng)的值都大于等于 needs 中的值,那么就可以知道當(dāng)前「窗口」符合要求了,可以開始移動(dòng) left 指針了。

現(xiàn)在將上面的框架繼續(xù)細(xì)化:

圖片

上述代碼已經(jīng)具備完整的邏輯了,只有一處偽碼,即更新最短子串結(jié)果 res 的地方,不過這個(gè)問題太好解決了,直接看完整解法吧!

圖片

如果直接甩給你這么一大段代碼,我想你的心態(tài)是爆炸的。但是通過之前的步步跟進(jìn),你應(yīng)該能夠理解這個(gè)算法的內(nèi)在邏輯,能清晰看出該算法的結(jié)構(gòu)了。

這個(gè)算法的時(shí)間復(fù)雜度是 O(M + N),M 和 N 分別是字符串 S 和 T 的長(zhǎng)度。因?yàn)槲覀兿扔?for 循環(huán)遍歷了字符串 T 來初始化 needs,時(shí)間 O(N),之后的兩個(gè) while 循環(huán)最多執(zhí)行 2M 次,時(shí)間 O(M)。

讀者也許認(rèn)為嵌套的 while 循環(huán)復(fù)雜度應(yīng)該是平方級(jí),但是你這樣想,while 執(zhí)行的次數(shù)就是雙指針 left 和 right 走的總路程,最多是 2M 嘛。

二、找到字符串中所有字母異位詞

圖片

這道題的難度是 Easy,但是評(píng)論區(qū)點(diǎn)贊最多的一條是這樣:

How can this problem be marked as easy?

實(shí)際上,這個(gè) Easy 是屬于了解雙指針技巧的人的,只要把上一道題的代碼改中更新結(jié)果的代碼稍加修改就成了這道題的解:

圖片

因?yàn)檫@道題和上一道的場(chǎng)景類似,也需要 window 中包含串 t 的所有字符,但上一道題要找長(zhǎng)度最短的子串,這道題要找長(zhǎng)度相同的子串,也就是「字母異位詞」嘛。

三、無重復(fù)字符的最長(zhǎng)子串

圖片

此題難度 Medium,遇到子串問題,首先想到的就是滑動(dòng)窗口技巧。

類似之前的思路,使用 window 作為計(jì)數(shù)器記錄窗口中的字符出現(xiàn)次數(shù),然后先向右移動(dòng) right,當(dāng) window 中出現(xiàn)重復(fù)字符時(shí),開始移動(dòng) left 縮小窗口,如此往復(fù):

圖片

需要注意的是,因?yàn)槲覀円蟮氖亲铋L(zhǎng)子串,所以需要在每次移動(dòng) right 增大窗口時(shí)更新 res,而不是像之前的題目在移動(dòng) left 縮小窗口時(shí)更新。

四、模板總結(jié)

通過上面三道題,我們可以總結(jié)出滑動(dòng)窗口算法的抽象思想:

int left = 0, right = 0;

while (right < s.size()) {
    window.add(s[right]);
    right++;

    while (valid) {
        window.remove(s[left]);
        left++;
    }
}

其中 window 的數(shù)據(jù)類型可以視具體情況而定,比如上述題目都使用哈希表充當(dāng)計(jì)數(shù)器,當(dāng)然你也可以用一個(gè)數(shù)組實(shí)現(xiàn)同樣效果,因?yàn)槲覀冎惶幚碛⑽淖帜浮?/p>

另外,滑動(dòng)窗口技巧也可以運(yùn)用在數(shù)組中,比如給一個(gè)數(shù)組,求其中 sum 最大的子數(shù)組,或者求平均數(shù)最大的子數(shù)組,都可以用滑動(dòng)窗口技巧解決。

稍微麻煩的地方就是這個(gè) valid 條件,為了實(shí)現(xiàn)這個(gè)條件的實(shí)時(shí)更新,我們可能會(huì)寫很多代碼。比如前兩道題,看起來解法篇幅那么長(zhǎng),實(shí)際上思想還是很簡(jiǎn)單,只是大多數(shù)代碼都在處理這個(gè)問題而已。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4599

    瀏覽量

    92643
  • 滑動(dòng)窗口法
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    2147
  • leetcode
    +關(guān)注

    關(guān)注

    0

    文章

    20

    瀏覽量

    2311
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    TCP協(xié)議擁塞控制的滑動(dòng)窗口協(xié)議解析

    TCP協(xié)議作為一個(gè)可靠的面向流的傳輸協(xié)議,其可靠性和流量控制由滑動(dòng)窗口協(xié)議保證,而擁塞控制則由控制窗口結(jié)合一系列的控制算法實(shí)現(xiàn)。
    的頭像 發(fā)表于 10-08 17:04 ?2895次閱讀
    TCP協(xié)議擁塞控制的<b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口</b>協(xié)議解析

    窗口問題

    程序包含一個(gè)主界面和一個(gè)窗口窗口用于實(shí)時(shí)更新狀態(tài)信息,更新頻率較為頻繁。目前窗口作為一個(gè)
    發(fā)表于 02-08 14:58

    請(qǐng)問SetParent綁架的窗口如何釋放

    用SetParent函數(shù)將窗口綁架到母窗口沒有問題,也可以對(duì)被綁架的窗口進(jìn)行設(shè)置調(diào)節(jié),但是在釋放的時(shí)候,
    發(fā)表于 09-14 10:38

    滑動(dòng)窗口

    假設(shè)D=double(1,515), A=cell(1,103),窗口長(zhǎng)度為25,滑動(dòng)距離為5,從D(1)開始滑動(dòng)窗口直到D的末尾,右端缺失部分補(bǔ)零,共得到103個(gè)
    發(fā)表于 11-22 11:22

    Delphi教程_從外部DLL中調(diào)用窗口

    Delphi教程從外部DLL中調(diào)用窗口,很好的Delphi資料,快來下載學(xué)習(xí)吧。
    發(fā)表于 03-16 14:49 ?5次下載

    基于滑動(dòng)窗口法的智能開關(guān)動(dòng)作時(shí)間動(dòng)態(tài)預(yù)測(cè)_鄭貴林

    基于滑動(dòng)窗口法的智能開關(guān)動(dòng)作時(shí)間動(dòng)態(tài)預(yù)測(cè)_鄭貴林
    發(fā)表于 01-18 20:21 ?0次下載

    基于滑動(dòng)窗口的多核程序數(shù)據(jù)競(jìng)爭(zhēng)硬件檢測(cè)算法

    數(shù)據(jù)競(jìng)爭(zhēng)是引起多核程序發(fā)生并發(fā)錯(cuò)誤的主要原因。針對(duì)現(xiàn)有基于硬件的happens-before數(shù)據(jù)競(jìng)爭(zhēng)檢測(cè)方法硬件開銷大的問題,提出了一種輕量級(jí)的內(nèi)存競(jìng)爭(zhēng)硬件檢測(cè)算法,該算法利用滑動(dòng)窗口
    發(fā)表于 02-07 13:33 ?0次下載
    基于<b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口</b>的多核程序數(shù)據(jù)競(jìng)爭(zhēng)硬件檢測(cè)<b class='flag-5'>算法</b>

    對(duì)于絕緣,它的電壓分布規(guī)律是什么

    在使用絕緣測(cè)試儀系列產(chǎn)品對(duì)日常運(yùn)行中的絕緣實(shí)際巡檢、維護(hù)中,我們可以根據(jù)絕緣的電壓分布規(guī)律及安裝使用環(huán)境來決定檢測(cè)絕緣
    的頭像 發(fā)表于 03-30 21:48 ?1.4w次閱讀

    快慢指針、左右指針的常見算法

    技巧秒殺 5 道算法題。 其實(shí),鼎鼎有名的「滑動(dòng)窗口算法」就是一種雙指針技巧,我們之前的爆文我寫了套框架,把滑動(dòng)窗口算法變成了默寫題就有這么
    的頭像 發(fā)表于 11-26 14:09 ?2442次閱讀

    基于MBNS滑動(dòng)窗口的多標(biāo)量乘快速算法

      針對(duì)橢圓曲線密碼體制中標(biāo)量乘與多標(biāo)量乘運(yùn)算耗時(shí)過長(zhǎng)的問題,設(shè)計(jì)以2、3、7為基元的多基整數(shù)表示方法,并結(jié)合多基數(shù)系統(tǒng)(MBNS)及滑動(dòng)窗口算法,提出基于MBNS滑動(dòng)窗口( Slid
    發(fā)表于 03-11 11:17 ?18次下載
    基于MBNS<b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口</b>的多標(biāo)量乘快速<b class='flag-5'>算法</b>

    基于滑動(dòng)窗口的寬度優(yōu)先搜索算法

    數(shù)據(jù)。針對(duì)此類數(shù)據(jù),設(shè)計(jì)基于滑動(dòng)窗口、 Apriori性質(zhì)和貪心選擇策略的寬度優(yōu)先搜索算法,對(duì)移動(dòng)對(duì)象伴隨模式挖掘問題進(jìn)行求解。同時(shí)結(jié)合基于哈希的迭代剪枝算法和基于摘要信息的剪枝
    發(fā)表于 04-27 14:14 ?4次下載
    基于<b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口</b>的寬度優(yōu)先搜索<b class='flag-5'>算法</b>

    labview主窗口窗口之間的切換

    labview主窗口窗口之間的切換
    發(fā)表于 11-01 15:55 ?73次下載

    語音芯片在口算訓(xùn)練儀的應(yīng)用

    ,完全替代了傳統(tǒng)口算練習(xí)冊(cè),如同玩游戲一般輕松練習(xí)口算,也節(jié)省了家長(zhǎng)檢查對(duì)錯(cuò)的時(shí)間。 口算訓(xùn)練儀擁有多個(gè)按鍵組成,每個(gè)按鍵對(duì)應(yīng)不同的數(shù)字與功能,內(nèi)有加減乘數(shù)等算法,通過語音播報(bào)的
    發(fā)表于 06-24 15:01 ?640次閱讀

    關(guān)于go語言實(shí)現(xiàn)的幾種限流算法介紹

    滑動(dòng)時(shí)間窗口算法,是從對(duì)普通時(shí)間窗口計(jì)數(shù)的優(yōu)化。使用普通時(shí)間窗口時(shí),我們會(huì)為每個(gè)user_id/ip維護(hù)一個(gè)KV: uidOrIp: timestamp_requestCount。
    發(fā)表于 04-01 10:37 ?606次閱讀

    滑動(dòng)窗口算法技巧

    說起滑動(dòng)窗口算法,很多讀者都會(huì)頭疼。這個(gè)算法技巧的思路非常簡(jiǎn)單,就是維護(hù)一個(gè)窗口,不斷滑動(dòng),然后更新答案么。LeetCode 上有起碼 10
    的頭像 發(fā)表于 04-19 10:55 ?881次閱讀
    <b class='flag-5'>滑動(dòng)</b><b class='flag-5'>窗口算法</b>技巧