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. 女友用python写的充值卡冲话费的代码

    女友最近在学习python ,于是我拿当初我们学习C语言的课程设计 手机号充话费功能让她尝试着做一下,在分析完思路和帮助改错的情况下代码如下: python2.7.17 环境 #/usr/bin/py ...

  2. ElementUI ---- dialog点击取消后蒙遮层不消失

    场景: 页面A打开了 dialog, 然后点击 页面A dialog 的按钮 跳转到 页面B,并且打开页面B的 dialog 但是页面B的 dialog 关闭后,蒙遮层并没消失(已经设置了 :appe ...

  3. curl访问etcd报错unable to set private key file

    [appdeploy@1a32vla0168zzzz cfssl]$ curl -i https://xxx.xxx.xxx.5:2379/health --cacert /app/etcd/cfss ...

  4. (Python基础教程之十四)Python将tuple开箱为变量或参数

    Python示例将N元素元组或序列开箱缩为N个变量的集合.将元组开箱缩为变量的 Python示例. 1. Python开箱元组示例 可以使用简单的赋值操作将任何序列(或可迭代)开箱缩为变量.唯一的要求 ...

  5. npm之基本使用

    # 查看镜像源 npm config get registry # 设置镜像源 # 腾讯云 npm config set registry http://mirrors.cloud.tencent.c ...

  6. dotnet core微服务框架Jimu ~ 基础网关

    网关提供外部访问内部微服务的统一入口,基于分布式和服务治理等功能特点,外部不能绕过网关调用内部微服务(框架本身提供外部可以直接访问内部微服务的功能,这里不作详细说明),外部通过 http 协议请求网关 ...

  7. kettle 使用 CARTE 执行

    在执行KETTLE 任务的时候,可以使用本地执行,或者使用carte server执行. 1.启动carte server .\Carte.bat localhost 8080 2.配置子服务器 这里 ...

  8. js修改表单值不会触发input事件

    前情 最近在做需求开发时候,需要监听表单的input事件 坑位 当通过JS动态修改表单的值的发现并不会触发表单的input事件 Why? 个人猜测是浏览器默认行为,input只会针对用户手动输入做响应 ...

  9. manim边做边学--淡入淡出

    本篇介绍Manim中的淡入和淡出动画效果. 淡入FadeIn 主要用于让对象以渐变的方式在场景中显现. 它的特点是视觉上柔和过渡,能自然地引导观众注意新出现的元素. 淡出FadeOut 则是使对象逐渐 ...

  10. arm mattermost

    It's not so hard, here is my working steps for arm64 device. cd ~/build/mattermost wget https://raw. ...