力扣(串聯所有單詞的子串)

串聯所有單詞的子串問題:多滑動窗口與哈希表的實戰應用。

一、題目分析

在這里插入圖片描述

(一)問題定義

給定字符串 s 和字符串數組 wordswords 中所有單詞長度相同 ),找出 s 中所有“串聯子串”的起始索引。串聯子串指包含 words 中所有單詞(任意順序連接)的子串。例如 words = ["ab","cd","ef"] 時,"abcdef""abefcd" 等符合要求,而順序混亂或缺失單詞的子串則不符合。

(二)核心挑戰

  1. 精確匹配:需確保子串包含 words 中所有單詞,且數量一致,順序不限。
  2. 高效遍歷s 可能較長,words 單詞數量和長度各異,需避免暴力枚舉所有可能子串(時間復雜度過高 )。
  3. 時間復雜度控制:題目要求高效算法,需利用哈希表和滑動窗口思想,將時間復雜度優化到可接受范圍。

二、算法思想:多滑動窗口 + 哈希表協同

(一)哈希表的作用

  • 詞頻統計:用哈希表 wordMap 統計 words 中各單詞的出現次數,作為匹配依據。
  • 窗口詞頻對比:在滑動窗口過程中,維護當前窗口內的單詞詞頻哈希表(windows 數組中的每個 Map ),與 wordMap 對比,判斷窗口是否為串聯子串。

(二)多滑動窗口的設計

由于 words 中單詞長度固定(設為 wordLen ),子串需由連續的單詞拼接而成。因此,以 wordLen 為間隔,設計 wordLen 個滑動窗口(對應不同起始偏移 )。例如 wordLen = 3 時,窗口起始偏移為 0、1、2 ,分別處理不同起始位置的子串,確保覆蓋所有可能的串聯子串。

(三)滑動窗口的移動策略

  1. 初始化窗口:對每個起始偏移 i0 <= i < wordLen ),初始化一個窗口,截取 s 中對應位置的所有單詞(按 wordLen 分割 ),統計詞頻存入 windows[i]
  2. 動態移動窗口:窗口初始化后,以 wordLen 為步長向右滑動。每次滑動時,移除窗口左側的舊單詞,加入右側的新單詞,更新 windows[winIndex] 的詞頻,再與 wordMap 對比,判斷是否為串聯子串。

三、代碼實現與詳細注釋

class Solution {public List<Integer> findSubstring(String s, String[] words) {int wordCount = words.length;int wordLen = words[0].length();int sLen = s.length();List<Integer> res = new ArrayList<>();//如果words的長度大于s的長度,則不可能有子串if (sLen < wordCount * wordLen || s == null || sLen == 0 || words == null || wordCount == 0) {return res;}//將word置入wordMap,用于比對和計數Map<String, Integer> wordMap = new HashMap<>();for (String word : words) {//如果word存在就在原有的數量上+1不存在為0+1;wordMap.put(word, 1 + wordMap.getOrDefault(word, 0));}//采用多滑動窗口的方式,一個下標表示一個滑動窗口Map<String, Integer>[] windows;windows = new HashMap[wordLen];//初始化多滑動窗口 i為windows中的每一個窗口下標//wordCount*wordLen是滑動窗口的大小//i+wordCount*wordLen確保當前起始位置 i 之后,存在足夠長度的子串for (int i = 0; i < wordLen && i + wordCount * wordLen <= sLen; i++) {// 在外層循環中初始化每個窗口的Mapwindows[i] = new HashMap<>();//提取對應滑動窗口內的所有單詞/*j=i,例:i=0即該滑動窗口是0偏移量的單詞i=1即該滑動窗口是1偏移量的單詞*///退出條件:j要保持在對應滑動窗口的大小中//j+=wordLen: 每次遞增一個單詞的長度 for (int j = i; j < i + wordCount * wordLen; j += wordLen) {String subStr = s.substring(j, j + wordLen); // j是當前單詞的起始索引//對字符串進行截取,截取為一個單詞一個單詞windows[i].put(subStr, 1 + windows[i].getOrDefault(subStr, 0));}//判斷每一個滑動窗口有沒有窗口已經是子串if (windows[i].equals(wordMap)) {res.add(i);}}//移動窗口//i代表窗口的左邊界//在上面已經對窗口進行初始化,起始位置從第一個窗口的下一個單詞長度開始//i+wordCount*wordLen<=sLen:確保當前位置i之后有足夠的長度容納整個窗口for (int i = wordLen; i + wordCount * wordLen <= sLen; i++) {//滑動窗口的相對位置int winIndex = i % wordLen;// s.substring(i,wordLen+j)// 截取左側單詞(起始位置:i - wordLen,長度:wordLen)String pervWord = s.substring(i - wordLen, (i - wordLen) + wordLen);// 截取右側新單詞(起始位置:nextWordStart,長度:wordLen)int nextWordStart = i + (wordCount - 1) * wordLen;String nextWord = s.substring(nextWordStart, nextWordStart + wordLen);//刪除左側單詞:如果在哈希表中值>1則這個word出現了1次以上,要在原值的基礎上-1,而不是直接刪除if(windows[winIndex].get(pervWord)>1){windows[winIndex].put(pervWord,windows[winIndex].get(pervWord)-1);}else{windows[winIndex].remove(pervWord); }//加入右側單詞windows[winIndex].put(nextWord, 1 + windows[winIndex].getOrDefault(nextWord, 0));//判斷每一個滑動窗口有沒有窗口已經是子串if (windows[winIndex].equals(wordMap)) {res.add(i);}}return res;}
}

(一)代碼流程拆解

  1. 初始化與邊界處理
    • 計算 wordCount(單詞數量 )、wordLen(單詞長度 )、sLens 長度 )。
    • s 長度不足容納 words 所有單詞拼接(sLen < wordCount * wordLen ),直接返回空結果。
  2. 構建 wordMap:統計 words 中各單詞的詞頻,用于后續匹配。
  3. 多滑動窗口初始化
    • 針對每個起始偏移 i0 ~ wordLen-1 ),初始化窗口的詞頻表 windows[i]
    • 截取 s 中對應窗口的所有單詞,統計詞頻。
    • 若窗口詞頻與 wordMap 匹配,記錄起始索引 i
  4. 滑動窗口處理
    • i = wordLen 開始,繼續滑動窗口。
    • 計算當前窗口的偏移索引 winIndexi % wordLen )。
    • 移除窗口左側舊單詞,更新詞頻;加入右側新單詞,更新詞頻。
    • 對比當前窗口詞頻與 wordMap ,匹配則記錄起始索引 i

(二)關鍵邏輯解析

  • 多窗口設計:因單詞長度固定,不同起始偏移(0 ~ wordLen-1 )的子串需獨立處理。例如 wordLen=3 時,起始偏移 0、1、2 對應子串起始為 0、1、2 ,需分別用窗口覆蓋。
  • 窗口詞頻更新:滑動時,通過“移除左側舊單詞、加入右側新單詞”的方式,避免重復截取子串(優化時間復雜度 )。利用哈希表的增刪操作,高效維護窗口內的詞頻。
  • 匹配判斷:每次窗口更新后,直接對比 windows[winIndex]wordMap ,利用哈希表的 equals 方法快速判斷是否匹配。

三、復雜度分析

(一)時間復雜度

  • 初始化多窗口:共 wordLen 個窗口,每個窗口截取 wordCount 個單詞(每個單詞截取時間為 O(wordLen) ),總時間復雜度為 O(wordLen * wordCount * wordLen) = O(wordLen2 * wordCount)
  • 滑動窗口處理:共 sLen - wordCount * wordLen 次滑動(近似 O(sLen) ),每次滑動涉及哈希表的增刪查操作(均為 O(1) ,單詞數量固定 ),總時間復雜度為 O(sLen)
  • 整體時間復雜度:O(wordLen2 * wordCount + sLen) 。因 wordLenwordCount 通常遠小于 sLen ,可近似認為是 O(sLen) ,滿足題目高效要求。

(二)空間復雜度

  • 哈希表存儲wordMap 存儲 wordCount 個單詞的詞頻,windows 數組存儲 wordLen 個窗口的詞頻(每個窗口最多 wordCount 個單詞 ),空間復雜度為 O(wordCount + wordLen * wordCount) = O(wordLen * wordCount)
  • 額外空間:結果列表 res 存儲符合條件的起始索引,最多 O(sLen / wordLen) 個元素。整體空間復雜度為 O(wordLen * wordCount + sLen / wordLen) ,可接受。

串聯所有單詞的子串問題,通過多滑動窗口 + 哈希表的協同策略,巧妙解決了精確匹配與高效遍歷的難題。多窗口覆蓋不同起始偏移,確保不遺漏可能的子串;哈希表實現詞頻快速統計與對比,優化匹配效率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93010.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RH134 管理基本存儲知識點

1. 對 Linux 磁盤進行分區時有哪兩種方案&#xff1f;分別加以詳細說明。答&#xff1a;MBR分區&#xff1a;主引導記錄(MBR)分區方案是運行BIOS固件的系統上的標準方案。此方案支持最 多四個主分區。在Linux系統上&#xff0c;您可以使用擴展分區和邏輯分區來創建最多…

【JS 異步】告別回調地獄:Async/Await 和 Promise 的優雅實踐與錯誤處理

【JS 異步】告別回調地獄&#xff1a;Async/Await 和 Promise 的優雅實踐與錯誤處理 所屬專欄&#xff1a; 《前端小技巧集合&#xff1a;讓你的代碼更優雅高效 上一篇&#xff1a; 【JS 數組】數組操作的“瑞士軍刀”&#xff1a;精通 Array.reduce() 的騷操作 作者&#xff…

23.Linux : ftp服務及配置詳解

Linux &#xff1a; ftp服務及配置詳解 FTP 基本概念 定義&#xff1a;文件傳輸協議&#xff08;File Transfer Protocol&#xff09;&#xff0c;采用 C/S 模式工作。端口&#xff1a; 控制端口&#xff1a;21數據端口&#xff1a;20FTP 工作原理模式工作流程連接發起方主動模…

悲觀鎖樂觀鎖與事務注解在項目實戰中的應用場景及詳細解析

在今天做的項目練習部分中真的學到了很多東西&#xff0c;也補充了許多之前遺漏或是忘記的知識點&#xff0c;但時間精力有限&#xff0c;我就先記錄一下今天用到的一個新東西&#xff0c;悲觀鎖和樂觀鎖。首先給出實際應用背景&#xff1a;在加入鎖和事務注解之前&#xff0c;…

Java構造器與工廠模式(靜態工程方法)詳解

1. 構造器1.1 構造器的核心意義1.1.1 對象初始化構造器在創建對象 (new) 時自動調用, 用于初始化對象的狀態 (如設置字段初始值, 分配資源等)無構造器時: 字段為默認值&#xff08;0/null/false&#xff09;有構造器&#xff1a;確保對象創建后即處于有效狀態1.1.2 強制初始化…

解決jdk初始化運行,防火墻通信選錯專業網絡問題

問題描述新項目添加不同版本的jdk&#xff0c;運行時提示防火墻通信策略&#xff0c;選成專用網絡。其他人訪問后端接口時&#xff0c;提示連接失敗。 解決方案&#xff1a;1、在搜索欄中輸入 防火墻關鍵字&#xff0c;選擇到防火墻和網絡保護2、選擇允許應用通過防火墻3、先點…

【Linux】常用命令(三)

【Linux】常用命令&#xff08;三&#xff09;1. export1.1 原理1.2 常用語法1.3 示例1.4 書中對命令的解釋1.5 生效范圍2. 測試服務地址與其端口能否訪問2.1 nc(Netcat)命令2.2 telnet2.3 nmap2.4 curl命令 (適用于HTTP/HTTPS 服務)1. export export 是 Linux Shell&#xff…

Pytest項目_day15(yaml)

YAMLYAML是一個對所有編程語言都很友好的數據序列化標準&#xff0c;它是一種直觀的能夠被電腦識別的數據序列化格式&#xff0c;是一種可讀性高且容易被人類閱讀的腳本語言YAML語言的本質是一種通用的數據串行化格式適用場景 可以直接序列化為數組、字典解析成本低專門寫配置文…

審批流程系統設計與實現:狀態驅動、靈活擴展的企業級解決方案

審批流程系統設計與實現&#xff1a;狀態驅動、靈活擴展的企業級解決方案 本文基于實際企業級審批系統源碼&#xff0c;深入解析如何設計高擴展性、強一致性的審批流程引擎&#xff0c;涵蓋狀態機設計、多租戶隔離、文件服務集成等核心實現。 1. 系統設計概覽 審批系統的核心架…

汽車免拆診斷案例 | 2010款奧迪A4L車行駛中發動機偶爾自動熄火

故障現象 一輛2010款奧迪A4L車&#xff0c;搭載CDZ發動機 &#xff0c;累計行駛里程約為18.2萬km。該車行駛中發動機偶爾自動熄火&#xff0c;有時熄火后能夠立即重新起動著機&#xff0c;有時需要等待一會兒才能重新起動著機&#xff0c;故障頻率較低。因該故障在其他維修廠陸…

Liam ERD:自動生成美觀的交互式實體關系圖

Liam ERD 是一個可以快速生成美觀且具有交互性的數據庫實體關系圖&#xff08;ERD&#xff09;的工具&#xff0c;可以幫助用戶實現復雜數據庫結構的可視化。 Liam ERD 是一個免費開源的項目&#xff0c;代碼托管在 GitHub&#xff1a; https://github.com/liam-hq/liam 功能…

網絡協議序列化工具Protobuf

目錄前言一、下載注意二、解壓安裝三、Protobuf的使用1、創建.proto文件2、利用protoc編譯.proto文件前言 Protocol Buffers是Google的?種語??關、平臺?關、可擴展的序列化結構數據的?法&#xff0c;它可?于&#xff08;數據&#xff09;通信協議、數據存儲等。 Protoco…

從表單校驗到API網關:全鏈路輸入安全防護指南

從表單校驗到 API 網關:全鏈路輸入安全防護指南 在軟件系統的安全防御體系中,輸入安全是第一道防線,而這道防線的堅固程度直接決定了系統抵御外部攻擊的能力。從用戶在瀏覽器中填寫表單的那一刻起,到數據經過 API 網關流轉至后端服務,每一個環節都可能成為輸入攻擊的突破…

Flask vs Django:微框架與一站式對決

Flask 簡介 1、簡介 Flask誕生于2010年&#xff0c;是Armin ronacher用Python語言基于Werkzeug工具箱編寫的輕量級Web開發框架&#xff0c;又稱之為微框架。 "微"的含義&#xff1a;Flask旨在保持核心簡潔&#xff0c;本身相當于內核&#xff0c;其他功能需通過擴展…

真實業務場景:mysql慢查詢優化(從17秒的查詢優化到700毫秒)

慢查詢業務場景:原先在我們系統中要統計一些人員的單位 部門信息的數據情況&#xff0c;比如總的男女人數&#xff0c;每個單位下的男女人數等等&#xff0c;然后原來的sql是這樣寫的 根據一個單位的id 然后對一張表做出多個子查詢進行查詢&#xff0c;這時候統計記錄 由于加載…

遠程影音訪問:通過 cpolar 內網穿透服務使用 LibreTV

文章目錄前言【視頻教程】1.關于LibreTV2.docker部署LibreTV3.簡單使用LibreTV4.安裝cpolar內網穿透5.配置ward公網地址6.配置固定公網地址總結LibreTV 與 cpolar 的協同應用&#xff0c;為用戶打造了一條通往高清觀影自由的便捷之路。通過這一方案&#xff0c;用戶不僅擺脫了商…

Apache ECharts 6 核心技術解密 – Vue3企業級可視化實戰指南

簡介 ECharts 是百度開源的一個使用 JavaScript 實現的開源可視化庫&#xff0c;它能夠生動、可交互地展示數據。在 Vue3 項目中集成 ECharts 可以讓你的項目更加直觀和動態地呈現數據信息。 核心優勢 特性SVG渲染器Canvas渲染器縮放保真度★★★★★★★☆☆☆動態交互性能…

考公VS考研,拼哪個性價比高?

即將到來下半年&#xff0c;將迎來考公和考研是兩個非常重要的考試&#xff0c;也是許多年輕人為之奮斗的目標。無論是獲得一份穩定的“鐵飯碗”&#xff0c;還是提升學歷學位獲得更高的競爭力&#xff0c;都是值得努力的方向。那么&#xff0c;考公vs考研&#xff0c;到底哪個…

python2操作neo4j

環境依賴 jdk、neo4j圖數據庫 操作一條數據完整demo import os,json,sys,io from py2neo import Graph,Nodetry:sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)sys.stderr io.TextIOWrapper(sys.stderr.buffer, encodingutf-8) except Exception:passcla…

AI 編程實踐:用 Trae 快速開發 HTML 貪吃蛇游戲

1. 背景與目標 貪吃蛇是最適合入門的 2D 網頁小游戲之一&#xff1a;規則簡單、反饋清晰、可擴展空間大&#xff08;穿墻模式、道具、多食物、排行榜……&#xff09;。 demo地址&#xff1a;https://game.haiyong.site/snake-game.html 本項目的目標是&#xff1a; 純前端、…