LeetCode 76 最小覆盖子串

点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)
更多干货,请关注公众号【忍者算法】,回复【刷题清单】获取完整题解目录~

从生活中理解这个问题

想象你是一位珠宝设计师,要用一段项链(可能包含各种宝石)找出最短的一段,这段中必须包含顾客指定的所有种类的宝石。比如顾客要求必须有红宝石、蓝宝石和钻石,你需要找出包含这三种宝石的最短项链片段。

这就是"最小覆盖子串"问题的生活映射:在一个长字符串中找到包含目标字符串所有字符的最短子串。

问题描述

LeetCode第76题是这样描述的:给你一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字符的最小子串。

举个例子:

S = "ADOBECODEBANC"
T = "ABC"
输出: "BANC" 解释:我们要找的子串必须包含A、B和C,而"BANC"是所有满足条件的子串中最短的。

解题套路:滑动窗口模板的应用

滑动窗口是一类特殊的双指针技巧,特别适合处理子串、子数组的问题。我们先来理解这个通用模板。

滑动窗口通用模板

// 通用的滑动窗口模板
public String slidingWindowTemplate(String s, String t) {
// 1. 定义窗口内的数据结构(通常是HashMap或数组)
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>(); // 2. 初始化目标条件
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
} // 3. 定义窗口的左右边界和控制变量
int left = 0, right = 0;
int valid = 0; // 用于判断条件是否满足 // 4. 循环扩大窗口
while (right < s.length()) {
// 5. 取出将要加入窗口的字符
char c = s.charAt(right);
right++; // 扩大窗口 // 6. 进行窗口内数据的一系列更新
... // 7. 判断左侧窗口是否要收缩
while (window needs shrink) {
// 8. 取出将要移出窗口的字符
char d = s.charAt(left);
left++; // 缩小窗口 // 9. 进行窗口内数据的一系列更新
...
}
}
return 最终结果;
}

这个模板的精髓在于:

  1. 两个指针:控制窗口的左右边界
  2. 数据结构:维护窗口内的状态
  3. 双层循环:外层扩展右边界,内层收缩左边界
  4. 更新规则:清晰的窗口数据更新规则

运用模板解决最小覆盖子串

现在让我们用这个模板来解决我们的问题:

public String minWindow(String s, String t) {
// 记录目标字符串中每个字符出现的次数
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
} // 记录窗口中的字符及其出现次数
Map<Character, Integer> window = new HashMap<>(); int left = 0, right = 0; // 窗口的左右边界
int valid = 0; // 已经匹配的字符个数 // 记录最小覆盖子串的起始位置和长度
int start = 0, minLen = Integer.MAX_VALUE; while (right < s.length()) {
// 扩大窗口
char c = s.charAt(right);
right++; // 如果是需要的字符,更新窗口数据
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
// 如果这个字符的数量正好匹配需求,valid加1
if (window.get(c).equals(need.get(c))) {
valid++;
}
} // 当所有字符都匹配后,尝试缩小窗口
while (valid == need.size()) {
// 更新最小覆盖子串
if (right - left < minLen) {
start = left;
minLen = right - left;
} // 缩小窗口
char d = s.charAt(left);
left++; // 如果移出的是需要的字符,更新窗口数据
if (need.containsKey(d)) {
if (window.get(d).equals(need.get(d))) {
valid--;
}
window.put(d, window.get(d) - 1);
}
}
} return minLen == Integer.MAX_VALUE ? ""
: s.substring(start, start + minLen);
}

让我们用一个简单的例子来详细演示这个过程:

S = "ADOBEC"
T = "ABC" 1. 初始状态:
need = {A:1, B:1, C:1}
window = {}
valid = 0 2. 遇到'A':
window = {A:1}
valid = 1 // A达到要求 3. 遇到'D':
不是需要的字符,跳过 4. 遇到'O':
不是需要的字符,跳过 5. 遇到'B':
window = {A:1, B:1}
valid = 2 // B达到要求 6. 遇到'E':
不是需要的字符,跳过 7. 遇到'C':
window = {A:1, B:1, C:1}
valid = 3 // 所有字符都达到要求了 8. 开始收缩窗口...

滑动窗口解题模板的四个重点

  1. 窗口定义

    • 明确窗口应该包含什么
    • 明确什么时候扩大窗口
    • 明确什么时候缩小窗口
  2. 状态变量

    • 使用合适的数据结构记录状态
    • 明确状态的更新规则
    • 明确有效状态的判断条件
  3. 更新规则

    • 扩大窗口时如何更新
    • 缩小窗口时如何更新
    • 什么时候更新结果
  4. 边界条件

    • 初始化值的设置
    • 结果不存在的处理
    • 特殊情况的考虑

类似题目及解题思路

这个模板可以解决很多类似的问题:

  • 字符串的排列
  • 找到字符串中所有字母异位词
  • 无重复字符的最长子串

解决这类问题的通用步骤:

  1. 确定是否适合用滑动窗口
  2. 定义窗口的意义
  3. 确定状态变量和更新规则
  4. 套用模板编写代码
  5. 处理边界条件

小结

掌握滑动窗口模板,就像学会了一把万能钥匙,可以解开许多字符串子串问题的大门。记住:

  1. 模板的核心是状态的维护和更新
  2. 左右指针的移动要有明确的逻辑
  3. 条件的判断要准确无误

下次遇到子串相关的问题,不妨先想想是否可以用这个模板来解决!


作者:忍者算法
公众号:忍者算法

滑动窗口模板在字符串中的巧妙应用|LeetCode 76 最小覆盖子串的更多相关文章

  1. [LeetCode] 76. 最小覆盖子串 ☆☆☆☆☆(滑动窗口)

    https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong- ...

  2. [LeetCode]438. 找到字符串中所有字母异位词、76. 最小覆盖子串(滑动窗口解决子串问题系列)

    题目438. 找到字符串中所有字母异位词 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引. 说明: 字母异位词指字母相同,但排列不同的字符 ...

  3. 《程序员代码面试指南》第五章 字符串问题 去掉字符串中连续出现k 个0 的子串

    题目 去掉字符串中连续出现k 个0 的子串 java代码 package com.lizhouwei.chapter5; /** * @Description: 去掉字符串中连续出现k 个0 的子串 ...

  4. 字符串问题----去掉字符串中连续出现K个0的子串

    去掉字符串中连续出现K个0的子串 给定一个字符串str,和一个整数k, 如果str中正好有连续K 个'0'字符出现,把连续的 k 个 '0'去掉,返回处理后的子串. [解题思路] 1. 定义两个变量, ...

  5. 【每日一题】【map操作】【滑动窗口所需元素】2021年12月22日-76. 最小覆盖子串

    给你一个字符串 s .一个字符串 t .返回 s 中涵盖 t 所有字符的最小子串.如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" . 注意: 对于 t 中重复字符 ...

  6. 字符串问题之 去掉字符串中连续出现K个0的子串

    字符串中刚好出现K个连续的‘O’,则把K个连续‘O’字符去除,返回处理后的字符串 比如 str="AOOOOOBOOO"   k=3, 返回“AOOOOOB” 这个题的解决思路也有 ...

  7. [算法]去掉字符串中连续出现的k个0子串

    题目: 给定一个字符串str和一个整数k,如果str中正好有k个‘0’字符出现时,把k个连续的‘0’字符去除,返回处理后的字符串. 举例: str=”A00B”,k=2,返回“AB” str=”A00 ...

  8. 滑动窗口模板 leetcode 209题

        """ 给定一个含有 n 个正整数的数组和一个正整数 target . 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, nums ...

  9. poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数

    Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11469   Accepted: 379 ...

  10. 字符串问题:去掉字符串中连续出现 k 个 0 的子串

    [题目] 给定一个字符串 str 和 一个整数 k, 如果 str 中正好有连续 k 个 ‘0’ 字符出现时,把 k 个连续的 ‘0’ 字符去除,返回处理后的字符串. [举例] str="A ...

随机推荐

  1. Sickos1.1 详细靶机思路 实操笔记

    Sickos1.1 详细靶机思路 实操笔记 免责声明 本博客提供的所有信息仅供学习和研究目的,旨在提高读者的网络安全意识和技术能力.请在合法合规的前提下使用本文中提供的任何技术.方法或工具.如果您选择 ...

  2. Nuxt.js 应用中的 webpack:error 事件钩子

    title: Nuxt.js 应用中的 webpack:error 事件钩子 date: 2024/11/25 updated: 2024/11/25 author: cmdragon excerpt ...

  3. Java GC 调试手记

    摘要 本文记录GC调试的一次实验过程和结果. GC知识要点回顾 问题1:为什么要调试GC参数?在32核处理器的系统上,10%的GC时间导致75%的吞吐量损失.所以在大型系统上,调试GC是以小博大的不错 ...

  4. 抓包工具之Charles(windows)

    激活码:  https://www.zzzmode.com/mytools/charles/ 官方地址:https://www.charlesproxy.com/ PC端如何配置才能抓取到https请 ...

  5. ThreeJs-06详解灯光与阴影

    一.gsap动画库 1.1 基本使用和原理 首先直接npm安装然后导入 比如让一个物体,x轴时间为5s 旋转同理 动画的速度曲线,可以在官网的文档找到 1.2 控制动画属性与方法 当然这里面也有一些方 ...

  6. CentOS7.8安装k8s

    1, 安装 docker / kubelet # 在 master 节点和 worker 节点都要执行 \# 最后一个参数 1.20.6 用于指定 kubenetes 版本,支持所有 1.20.x 版 ...

  7. 【uni-app】【02】请求域名的全局配置。

    关于uni-app的请求问题,可以访问官方文档 https://uniapp.dcloud.net.cn/api/request/request.html 值得注意的一个点就是全局配置域名的问题. 因 ...

  8. WPF 记录鼠标、触摸多设备混合输入场景问题

    本文记录在WPF应用中鼠标.触摸混合输入,鼠标事件抬起时不会有MouseUp事件触发的问题. 事件输入我们都知道有3类:鼠标.触摸.触笔,鼠标是windows系统出来就有的事件,后面加了触笔.触摸. ...

  9. Python 进阶:深入理解 import 机制与 importlib 的妙用

    大家好,今天我们来深入探讨 Python 中的导入机制和 importlib 模块.相信不少朋友和我一样,平时写代码时可能只用过最基础的 import 语句,或者偶尔用 importlib.impor ...

  10. JMeter:强大的性能测试工具

    揭秘 JMeter:性能测试的得力助手 宝子们,今天咱来唠唠在软件测试这旮旯里超厉害的 JMeter!这玩意儿就像是个超级侦探,能帮咱摸清楚软件系统在人多手杂的时候到底表现咋样. 一.JMeter 是 ...