跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

滑动窗口算法实战:串联所有单词的子串与最小覆盖子串

滑动窗口算法实战:串联所有单词的子串与最小覆盖子串。针对串联所有单词的子串,将单词视为字符处理,控制步长遍历;对于最小覆盖子串,利用双哈希表动态维护窗口内字符频次,优化版使用定长数组提升效率。核心在于理解窗口收缩条件与状态更新逻辑。

蓝绿部署发布于 2026/3/16更新于 2026/10/1285 浏览
滑动窗口算法实战:串联所有单词的子串与最小覆盖子串

15. 串联所有单词的子串

题目描述: 给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好由 words 中所有单词串联组成的子串的起始索引。

解法(滑动窗口 + 哈希表):

算法思路

将每个单词视为一个整体字符,问题转化为寻找包含特定频次单词组合的区间。核心差异在于处理对象从字符变成了单词。

  • 哈希表结构:使用 unordered_map<string, int> 统计目标单词频次。
  • 步长控制:左右指针移动步长为单词长度 len。
  • 遍历次数:由于起始位置可能不同,需执行 len 次滑动窗口遍历,确保不遗漏任何偏移量。
C++ 算法代码
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ret;
        unordered_map<string, int> hash1; // 保存 words 里面所有单词的频次
        for (auto &w : words) hash1[w]++;
        
        int len = words[0].size(), m = words.size();
        if (m == 0 || len == 0 || s.empty()) return ret;

        // 执行 len 次,覆盖所有可能的起始偏移
        for (int i = 0; i < len; i++) {
            unordered_map<string, int> hash2; // 维护窗口内单词的频次
            int count = 0;
            
            for (int left = i, right = i; right + len <= s.size(); right += len) {
                string in = s.substr(right, len);
                hash2[in]++;
                
                // 进窗口 + 维护 count
                if (hash1.count(in) && hash2[in] <= hash1[in]) {
                    count++;
                }
                
                // 判断窗口是否过大
                if (right - left + 1 > len * m) {
                    string out = s.substr(left, len);
                    // 出窗口 + 维护 count
                    if (hash1.count(out) && hash2[out] <= hash1[out]) {
                        count--;
                    }
                    hash2[out]--;
                    left += len;
                }
                
                if (count == m) ret.push_back(left);
            }
        }
        return ret;
    }
};
算法总结

通过控制窗口大小和步长,配合哈希表统计频次,可以高效定位满足条件的子串。注意边界条件处理,如空字符串或空单词列表的情况。

文章配图


16. 最小覆盖子串

题目描述: 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果不存在这样的子串,返回空字符串。

解法(滑动窗口 + 哈希表):

算法思路

研究对象是连续区间,适合用滑动窗口。关键在于如何快速判断当前窗口是否满足要求。

  • 双哈希表策略:一个记录目标串 t 的字符频次,另一个动态维护窗口内的字符频次。
  • 匹配判定:当窗口内某字符数量达到目标频次时,该字符才算'有效'。当所有有效字符种类数等于 t 的种类数时,窗口即满足条件。
算法流程
  1. 初始化两个哈希表(或数组),分别统计 t 和窗口内容。
  2. 右指针向右扩展,更新窗口状态。
  3. 一旦满足条件,尝试收缩左指针以寻找更优解。
  4. 记录满足条件下的最小窗口起始位置和长度。
C++ 算法代码

初版(基于 unordered_map):

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> hash1; // 目标串 t 的信息
        for (auto &ch : t) hash1[ch]++;
        
        unordered_map<char, int> hash2; // 窗口信息
        int n = hash1.size(), m = s.size();
        int len = INT_MAX, reti = 0;
        int count = 0;

        for (int left = 0, right = 0; right < m; right++) {
            char in = s[right];
            hash2[in]++;
            
            // 进窗口 + 维护 count
            if (hash1.count(in) && hash2[in] == hash1[in]) {
                count++;
            }
            
            while (count == n) {
                // 更新结果
                if (right - left + 1 < len) {
                    len = right - left + 1;
                    reti = left;
                }
                
                // 出窗口 + 维护 count
                char out = s[left++];
                if (hash1.count(out) && hash2[out] == hash1[out]) {
                    count--;
                }
                hash2[out]--;
            }
        }
        return len == INT_MAX ? "" : s.substr(reti, len);
    }
};

优化版(定长数组): 针对 ASCII 字符,使用大小为 128 的数组替代哈希表,可显著提升性能。

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = {0}; // 统计字符串 t 中每一个字符的频次
        int kinds = 0; // 统计有效字符有多少种
        for (auto ch : t) {
            if (hash1[ch]++ == 0) kinds++;
        }
        
        int hash2[128] = {0}; // 统计窗口内每个字符的频次
        int minlen = INT_MAX, begin = -1;
        int count = 0;

        for (int left = 0, right = 0; right < s.size(); right++) {
            char in = s[right];
            if (++hash2[in] == hash1[in]) count++; // 进窗口 + 维护 count
            
            while (count == kinds) { // 判断条件
                if (right - left + 1 < minlen) {
                    minlen = right - left + 1;
                    begin = left;
                }
                char out = s[left++];
                if (hash2[out]-- == hash1[out]) count--; // 出窗口 + 维护 count
            }
        }
        return begin == -1 ? "" : s.substr(begin, minlen);
    }
};
算法总结

优化版利用定长数组避免了哈希表的开销,在字符集有限时效率更高。核心逻辑依然是维护 count 变量来代表当前窗口满足要求的字符种类数,避免每次循环都遍历整个哈希表。

文章配图

目录

  1. 15. 串联所有单词的子串
  2. 算法思路
  3. C++ 算法代码
  4. 算法总结
  5. 16. 最小覆盖子串
  6. 算法思路
  7. 算法流程
  8. C++ 算法代码
  9. 算法总结

更多推荐文章

查看全部
  • TRAE、Qoder、Cursor 与 Copilot:主流 AI 编程工具深度对比
  • Python 基础语法入门:常量、变量与运算符
  • FPGA 调试:PCIe XDMA 无法 Link Up 的 LTSSM 定位与解决
  • MetaAPP 前端一面面试题与原理深度解析
  • 数据结构:栈、队列及顺序表应用算法
  • Android 中高级技术面试核心考点与解析
  • C++ 异常机制详解
  • 数据结构:快速排序算法详解(双指针法)
  • Claude-Code 2.1.88 源码结构解析:基于 Source Map 的逆向分析
  • 基于 Docker 在 NVIDIA 5090 上部署 ComfyUI 与 Stable-Diffusion-Webui
  • 卷积神经网络(CNN)进阶:经典架构解析与实战开发
  • Spatial Joy 2025 全球 AR&AI 赛事参赛指南与赛道解析
  • Whisper 模型版本及下载链接
  • 云端多模型并行部署:Youtu-LLM 写作能力对比测试
  • Git 用户名与邮箱配置指南
  • Gemini 2.5 Pro 技术突破与实战应用深度解析
  • 基于 AnythingLLM 与 Ollama 构建本地私有 AI 知识库
  • AIGC 中的变分自编码器(VAE)原理及代码实现
  • AI 大模型的学习路径:理论构建、技术创新与应用实践
  • 基于多模态嵌入的 AI 搜索与 RAG 应用实现

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online