news 2026/5/26 0:24:05

双指针专题(八):步长跳跃的艺术——「串联所有单词的子串」

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
双指针专题(八):步长跳跃的艺术——「串联所有单词的子串」

场景想象:

你有一串很长的珍珠项链(字符串 s),和一堆散落的、长度相同的宝石(单词数组 words)。

你需要从项链上截取一段,使得这段子串 恰好 由所有的宝石串联而成(顺序不限,但每个宝石都要用上,且不能多不能少)。

难点升级:

  1. 步长变化:以前left++right++是挪动 1 个字符。现在,因为单词长度固定为k,我们每次必须挪动k个字符

  2. 分组遍历:如果我们只从index=0开始每次跳k步,我们会漏掉从index=1开始的情况。所以我们需要错位触发

力扣 30. 串联所有单词的子串

https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

题目分析:

  • 输入:字符串s,字符串数组wordswords里所有单词长度相同,记为oneWordLen)。

  • 目标:在s中找到所有起始索引,使得从该位置开始的子串恰好包含words中所有单词各一次。

  • 输出:索引数组。

核心思维:多路滑动窗口 + HashMap 计数

这其实就是 LC 438 异位词 的“巨人版”。

我们将 s 看作是一个由“单词”组成的数组。

假设单词长度 oneWordLen = 3。

我们需要分别从 0, 1, 2 这三个偏移量开始跑滑动窗口。

  • Offset 0: 检查s[0...2], s[3...5], s[6...8]...

  • Offset 1: 检查s[1...3], s[4...6], s[7...9]...

  • Offset 2: 检查s[2...4], s[5...7], s[8...10]...

这样就能覆盖所有可能的切分情况。

代码实现 (JavaScript)

JavaScript

/** * @param {string} s * @param {string[]} words * @return {number[]} */ var findSubstring = function(s, words) { if (!s || !words || words.length === 0) return []; const oneWordLen = words[0].length; const wordCount = words.length; // 总长度:所有单词拼起来有多长 const allWordsLen = oneWordLen * wordCount; const res = []; // 1. 统计 words 里的单词需求 const need = new Map(); for (const w of words) { need.set(w, (need.get(w) || 0) + 1); } // 2. 核心:外层循环控制“偏移量” (Offset) // 只要遍历 0 到 oneWordLen - 1 即可覆盖所有情况 // 比如单词长 3,我们只需要从 0, 1, 2 开始跑三次滑动窗口 for (let i = 0; i < oneWordLen; i++) { let left = i; let right = i; let count = 0; // 窗口内有效匹配的单词个数 const window = new Map(); // 当前窗口里的单词统计 // 内层循环:标准的滑动窗口,每次跳 oneWordLen 步 while (right + oneWordLen <= s.length) { // --- 进窗口 --- // 截取一个单词 const w = s.substring(right, right + oneWordLen); right += oneWordLen; // 指针大步跃进 // 如果这个单词不在需求列表里,说明这一段全废了 if (!need.has(w)) { // 直接清空窗口,从下一个位置重新开始 window.clear(); count = 0; left = right; } else { // 单词是有效的,加入窗口 window.set(w, (window.get(w) || 0) + 1); count++; // --- 出窗口 --- // 如果窗口里某个单词的数量超过了需求 // 比如 need={A:1},window={A:2},这时候必须缩左边,直到把多余的 A 吐出去 while (window.get(w) > need.get(w)) { const leftWord = s.substring(left, left + oneWordLen); window.set(leftWord, window.get(leftWord) - 1); count--; left += oneWordLen; // 左指针也大步跃进 } // --- 检查结果 --- // 如果单词数量够了,记录起始位置 if (count === wordCount) { res.push(left); } } } } return res; };

深度解析:为什么需要外层循环i

这是这道题最难理解的点。

假设 s = "barfoothefoobarman", words = ["foo", "bar"] (长度3)。

  • 如果不加外层循环,直接从 0 开始滑:

    • 检查bar,foo,the... (索引 0, 3, 6...)

  • 但是,如果答案是从索引 1 开始的呢?(虽然这题不是,但假设s = "afoo..."

    • 从 0 开始滑会读到afo,o...,完全错位。

  • 所以,我们需要:

    • i=0: 处理索引0, 3, 6, 9...的单词切分。

    • i=1: 处理索引1, 4, 7, 10...的单词切分。

    • i=2: 处理索引2, 5, 8, 11...的单词切分。

    • i=3: 不需要了,因为它和i=0是重合的(只是少了一个单词)。

总结

这道题是 LC 438 的进阶版,也是 LC 76 的变种。

它强迫你把“字符”的微观视角,转换成“单词块”的宏观视角。

  • 关键点 1:外层循环遍历0wordLen - 1,消除“相位差”。

  • 关键点 2:内层循环的步长是wordLen

  • 关键点 3:遇到非法单词直接window.clear(),这是极大的优化(剪枝)。


下一题预告:滑动窗口最大值

拿下了这道“步长跳跃”的难题,下一关我们要去挑战一个需要特殊武器的题目——LC 239. 滑动窗口最大值(Hard)。

  • 题目很简单:一个窗口滑过数组,每次告诉你窗口里最大的那个数是谁。

  • 陷阱:如果你每次遍历窗口找最大值,时间复杂度是 $O(N \times K)$,会超时。

  • 新武器:你需要学会使用“单调队列” (Monotonic Queue)。这是一种虽然叫“队列”,但里面的人必须按身高排队的神奇结构。

准备好解锁新数据结构了吗?🚀

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/21 1:05:19

清华镜像站rsync命令同步HunyuanOCR模型数据集

清华镜像站rsync命令同步HunyuanOCR模型数据集 在AI研发一线工作的人都深有体会&#xff1a;一个项目启动阶段最耗时的&#xff0c;往往不是写代码、调模型&#xff0c;而是“等下载”——尤其是面对动辄十几甚至上百GB的大模型权重文件。当你兴致勃勃地准备复现一篇论文或部署…

作者头像 李华
网站建设 2026/5/20 16:58:47

【资深架构师亲述】:我为何在高并发项目中放弃C++改用Rust(附性能对比图)

第一章&#xff1a;C在高并发系统中的历史地位与挑战C 自诞生以来&#xff0c;一直是构建高性能、低延迟系统的首选语言之一。其对底层硬件的直接控制能力、零成本抽象特性以及丰富的模板机制&#xff0c;使其在金融交易系统、实时通信平台和大型互联网后端服务中占据核心地位。…

作者头像 李华
网站建设 2026/5/20 23:34:00

C++高效加载大语言模型的4种方案对比,第3种竟节省50%资源

第一章&#xff1a;C AIGC 模型加载技术概述在人工智能生成内容&#xff08;AIGC&#xff09;领域&#xff0c;C凭借其高性能与底层控制能力&#xff0c;成为部署大规模模型的重要工具。模型加载作为推理流程的起点&#xff0c;直接影响系统的启动速度、内存占用和运行效率。现…

作者头像 李华
网站建设 2026/5/25 1:14:40

C#调用HunyuanOCR接口示例代码分享(基于HttpClient)

C# 调用 HunyuanOCR 接口实战&#xff1a;轻量大模型与企业应用的高效集成 在银行柜台&#xff0c;一名柜员将一张身份证放在扫描仪上&#xff0c;不到三秒&#xff0c;姓名、性别、身份证号等信息已自动填入业务系统&#xff1b;在医院档案室&#xff0c;上千份手写病历正被高…

作者头像 李华
网站建设 2026/5/22 14:00:28

Dify可视化编排调用HunyuanOCR API实现合同识别机器人

Dify可视化编排调用HunyuanOCR API实现合同识别机器人 在企业日常运营中&#xff0c;每天都有成百上千份合同、发票、证件等待处理。传统方式依赖人工逐字录入&#xff0c;效率低、易出错&#xff0c;尤其当文档格式多样、语言混杂时&#xff0c;更是苦不堪言。有没有一种方法&…

作者头像 李华
网站建设 2026/5/22 8:42:22

计算机毕业设计springboot玩具公司进销存管理系统 计算机毕业设计springboot玩具公司进销存管理系统 SpringBoot框架下的玩具公司库存、采购及销售一体化管理系统

计算机毕业设计springboot玩具公司进销存管理系统4bas39 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;传统玩具公司的进销存管理方式面临着…

作者头像 李华