(Java)leetcode-76 Minimum Window Substring(最小覆盖子串)

2023-11-19

题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

示例 2:
输入:s = "a", t = "a"
输出:"a"
 

提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成

思路:滑动窗口

如果我们使用暴力解法,代码大概是这样的:

for (int i = 0; i < s.size(); i++)
    for (int j = i + 1; j < s.size(); j++)
        if s[i:j] 包含 t 的所有字母:
            更新答案

思路很直接,但是显然,这个算法的复杂度肯定大于 O(N^2) 了,不好。
与leetcode-5相似,此题可用滑动窗口算法解决,从而实现O(N) 的时间复杂度。

滑动窗口算法的思路是这样:

1、在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间[left, right)称为一个「窗口」。

2、先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。

3、此时,停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同时,每次增加left,都要更新一轮结果。

4、重复第 2 和第 3 步,直到right到达字符串S的尽头。

这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是「滑动窗口」这个名字的来历。

滑动窗口算法框架

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
    HashMap<Character, Integer> need,window;

    for (int i = 0; i < t.length(); i++) {
    	// 初始化目标map
    }

    int left = 0, right = 0;
    int valid = 0; 

    while (right < s.length()) {
        // c 是将移入窗口的字符
        char c = s.charAt(right);
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新(增加window这个map中的计数)
        ...

        /*** debug 输出的位置 ***/
        System.out.printf("window: %d, %d", left, right);
        /********************/

        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s.charAt(left);
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新(减少window这个map中的计数)
            ...
        }
    }
    // 返回结果
}

本题中的滑动窗口

首先,初始化window和need两个哈希表,记录窗口中的字符需要凑齐的字符
然后,使用left和right变量初始化窗口的两端,不要忘了,区间[left, right)是左闭右开的,所以初始情况下窗口没有包含任何元素;
其中valid变量表示窗口中满足need条件的字符个数,如果valid和need.size的大小相同,则说明窗口已满足条件,已经完全覆盖了串T。

现在开始套模板,只需要思考以下四个问题:

1、当移动right扩大窗口,即加入字符时,应该更新哪些数据?
2、什么条件下,窗口应该暂停扩大,开始移动left缩小窗口?
3、当移动left缩小窗口,即移出字符时,应该更新哪些数据?
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?

如果一个字符进入窗口,应该增加window计数器;如果一个字符将移出窗口的时候,应该减少window计数器;当valid满足need时应该收缩窗口;应该在收缩窗口前更新最终结果。

下面是完整代码:

class Solution {
    public String minWindow(String s, String t) {
    	HashMap<Character, Integer> need = new HashMap<>();
    	HashMap<Character, Integer> window = new HashMap<>();
    	// 初始化目标字符
	    for (int i = 0; i < t.length(); i++) {
	    	need.put(t.charAt(i), need.getOrDefault(t.charAt(i), 0) + 1);
	    }

	    int left = 0, right = 0;
	    int valid = 0;
	    // 记录最小覆盖子串的起始索引及长度
	    int start = 0, len = Integer.MAX_VALUE;
	    while (right < s.length()) {
	        // c 是将移入窗口的字符
	        char c = s.charAt(right);
	        // 右移窗口
	        right++;
	        // 进行窗口内数据的一系列更新
	        if (need.containsKey(c)) {
	            window.put(c , window.getOrDefault(c, 0) + 1);
	            if (window.get(c).equals(need.get(c))) {
                    valid++;
                }                
	        }
            
	        // 判断左侧窗口是否要收缩
	        while (valid == need.size()) {
	            // 在这里更新最小覆盖子串
	            if (right - left < len) {
	                start = left;
	                len = right - left;
	            }
	            // d 是将移出窗口的字符
	            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 len == Integer.MAX_VALUE ?
        "" : s.substring(start, start+len);
    }
}

时间复杂度O(n)
空间复杂度O(c)c为字符集大小

这里需要注意的一点是【判断字符相等】,若使用window.get(char)== need.get(char),会出现leetcode最后一个测试用例【超长字符串】通不过的情况,要明白一件事:
Integer是对象!
Integer会缓存频繁使用的数值,数值范围为-128到127,在此范围内直接返回缓存值,超过该范围就会new 一个对象!此时用“==”判断就会出现字符一样但返回false的情况!
因此判断Integer相等应该用equals()

回到算法本身,当我们发现某个字符在window的数量满足了need的需要,就要更新valid,表示有一个字符已经满足要求。而且,你能发现,两次对窗口内数据的更新操作是完全对称的。

当valid == need.size()时,说明T中所有字符已经被覆盖,已经得到一个可行的覆盖子串,现在应该开始收缩窗口了,以便得到「最小覆盖子串」。

移动left收缩窗口前,窗口内的字符都是可行解,所以应该在收缩窗口的阶段进行最小覆盖子串的更新,以便从可行解中找到长度最短的最终结果。

用数组代替哈希表

class Solution {
    public String minWindow(String s, String t) {
    	int[] need = new int[128], window = new int[128];
    	int count = 0;
    	// 初始化目标字符
	    for (int i = 0; i < t.length(); i++) {
	    	need[t.charAt(i)]++;
	    }
	    int left = 0, right = 0;

	    // 记录最小覆盖子串的起始索引及长度
	    int start = 0, len = Integer.MAX_VALUE;
	    while (right < s.length()) {
	        // c 是将移入窗口的字符
	        char c = s.charAt(right);
	        // 右移窗口
	        right++;
	        // 进行窗口内数据的一系列更新
	        if (need[c] > 0) {
	            window[c] += 1;
	            if (window[c] < need[c]) {
                    count++;
                }                
	        }
            
	        // 判断左侧窗口是否要收缩
	        while (count == t.length()) {
	            // 在这里更新最小覆盖子串
	            if (right - left < len) {
	                start = left;
	                len = right - left;
	            }
	            // d 是将移出窗口的字符
	            char d = s.charAt(left);
	            // 左移窗口
	            left++;
	            // 进行窗口内数据的一系列更新
	            if (need[d] > 0) {
	                if (window[d] == need[d])
	                    count--;
	                window[d]--;
	            }                    
	        }
    }
    // 返回最小覆盖子串
    return len == Integer.MAX_VALUE ?
        "" : s.substring(start, start+len);
    }
}

空间复杂度O(1)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(Java)leetcode-76 Minimum Window Substring(最小覆盖子串) 的相关文章

  • Android:如何暂停和恢复可运行线程?

    我正在使用 postDelayed 可运行线程 当我按下按钮时 我需要暂停并恢复该线程 请任何人帮助我 这是我的主题 protected void animation music6 music4 postDelayed new Runnab
  • 同一服务器上的许多应用程序具有相同的 JMX Mbean 类

    我有超过 5 个 Spring Web 应用程序 它们都在使用另一个通用库 这个公共库有它自己的 MBean 由于强制的唯一 objectName 约束 我的应用程序无法部署在同一服务器上 我使用 MBean 的方式是这样的 Managed
  • 在Windows Server 2003下如何在本地系统帐户下运行jvisualvm.exe?

    我在带有 Java 1 6 u 20 的 Windows Server 2003 下将 GlassFish 3 0 1 作为 Windows 服务运行 总体上我很满意 我希望能够在这个 JVM 上使用 VisualVM 并使用无法在 Tom
  • 连接外部 Accumulo 实例和 java

    我正在尝试使用 Accumulo 连接到虚拟机 问题是 我无法将其连接到 Java 中 我可以看到 Apache 抛出的网页 但我无法让它与代码一起工作 我认为这是缺乏知识的问题而不是真正的问题 但我找不到这方面的文档 所有示例都使用 lo
  • 非易失性领域的出版与阅读

    public class Factory private Singleton instance public Singleton getInstance Singleton res instance if res null synchron
  • 如何以编程方式使用包含多列的 where-in 子句执行 PostgreSQL 查询?

    我的查询是这样的 select from plat customs complex where code t code s in 01013090 10 01029010 90 它在 psql 控制台中运行良好 我的问题是如何在客户端代码中
  • 查看Java Agent修改的Java类的源代码

    我需要了解 Java 代理如何修改我的初始类 以便我能够理解代码的作用 build gradle configurations jar archiveName agent2 jar jar manifest attributes Prema
  • 什么是内部类的合成反向引用

    我正在寻找应用程序中的内存泄漏 我正在使用的探查器告诉我寻找这些类型的引用 但我不知道我在寻找什么 有人可以解释一下吗 Thanks Elliott 您可以对 OUTER 类进行合成反向引用 但不能对内部类实例进行合成 e g class
  • 使用 OkHttp 下载损坏的文件

    我编写的下载文件的方法总是会产生损坏的文件 public static String okDownloadToFileSync final String link final String fileName final boolean te
  • getCurrentSession 在网络中休眠

    我正在使用 hibernate 和 jsp servlet 编写一个基于 Web 的应用程序 我读过有关sessionFactory getCurrentSession and sessionFactory openSession方法 我知
  • 在光标所在行强制关闭!

    嘿 我正在尝试创建一个应用程序来查找存储在 SQlite 数据库中的 GPS 数据 但我面临一个问题 我构建了一个 DbAdapter 类来创建数据库 现在我尝试使用以下函数从另一个类获取所有数据上的光标 public Cursor fet
  • IntelliJ Idea:将简单的 Java servlet(无 JSP)部署到 Tomcat 7

    我尝试按照教程进行操作here http wiki jetbrains net intellij Creating a simple Web application and deploying it to Tomcat部署 servlet
  • Joshua Bloch 的构建器设计模式有何改进?

    早在 2007 年 我就读过一篇关于 Joshua Blochs 所采用的 构建器模式 的文章 以及如何修改它以改善构造函数和 setter 的过度使用 特别是当对象具有大量属性 其中大部分属性是可选的 时 本文对此设计模式进行了简要总结
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 我所有的 java 应用程序现在都会抛出 java.awt.headlessException

    所以几天前我有几个工作Java应用程序使用Swing图书馆 JFrame尤其 他们都工作得很好 现在他们都抛出了这个异常 java awt headlessexception 我不知道是什么改变了也许我的Java版本不小心更新了 谢谢你尽你
  • 确定 JavaFX 中是否消耗了事件

    我正在尝试使用 JavaFX 中的事件处理来做一些非滑雪道的事情 我需要能够确定手动触发事件后是否已消耗该事件 在以下示例中 正确接收了合成鼠标事件 但调用 Consumer 不会更新该事件 我对此进行了调试 发现 JavaFX 实际上创建
  • Java 中清除嵌套 Map 的好方法

    public class MyCache AbstractMap
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • 如何让 Firebase 与 Java 后端配合使用

    首先 如果这个问题过于抽象或不适合本网站 我想表示歉意 我真的不知道还能去哪里问 目前我已经在 iOS 和 Android 上开发了应用程序 他们将所有状态保存在 Firebase 中 因此所有内容都会立即保存到 Firebase 实时数据
  • 在会话即将到期之前调用方法

    我的网络应用程序有登录的用户 有一个超时 在会话过期之前 我想执行一个方法来清理一些锁 我已经实现了sessionListener但一旦我到达public void sessionDestroyed HttpSessionEvent eve

随机推荐

  • 华为OD机试真题- 关联子串-2023年OD统一考试(B卷)

    题目描述 给定两个字符串str1和str2 如果字符串str1中的字符 经过排列组合后的字符串中 只要有一个字符串是str2的子串 则认为str1是str2的关联子串 若str1是str2的关联子串 请返回子串在str2的起始位置 若不是关
  • Python中保留两位小数的几种方法

    保留两位小数 并做四舍五入处理 方法一 使用字符串格式化 gt gt gt a 12 345 gt gt gt print 2f a 12 35 gt gt gt 方法二 使用round内置函数 gt gt gt a 12 345 gt g
  • VMware Fusion(虚拟机)免费下载正版授权(Mac升级到Big Sur后旧版VM显示物理内存不足)

    下载教程可以参考本博客 安装Linux系统教程可查询本人此博客 描述 MacbookPro在升级到macOS Big Sur后 无法打开VM虚拟机 打开时显示物理内存不足 如下图 所以在VM官网下载最新版VM虚拟机 12 1 1 个人免费正
  • 如何解决出现Unknown at rule @applyscss(unknownAtRules)警告?

    在nuxt框架当中使用tailwindcss出现 apply下面出现警告 怎么取消这个警告呢 安装 Stylelint 首先 我们需要安装Stylelint本身和一个包含合理标准配置的包 npm install save dev style
  • tensorflow1.14.0版本安装指南

    1 安装tensorflow1 14 0版本 注意不要安装GPU版本 注意一定要指定1 14 0版本号 1 python虚拟环境下输入 pip install tensorflow 1 14 0 i https pypi tuna tsin
  • SpringBoot整合JPA 数据库自动增加字段问题记录

    Spring整合JPA启动的时候忽然发现 数据增加了两个字段 我当时就很纳闷了 我自己写的有实体有字段 并且跟数据一致 为什么要给我增加两个字段哪 我的实体如下 启动的时候就变成这样了 然后就找度娘问原因 度娘告诉我JPA会把我们的驼峰命名
  • TensorFlowX.Y核心基础与AI模型设计01: TF模型从0到1搭建流程【加载数据、训练数据、保存模型、推理图像】

    上一篇文章 TensorFlowX Y核心基础与AI模型设计开篇 1 思想启蒙 mnist手写体从模型搭建到图片识别 通过手写字体认识模型的构建发整体流程 加载数据 构建模型 训练模型 保存模型 测试代码 import tensorflow
  • 力扣leecode-python解法笔记之202. 快乐数

    class Solution object def isHappy self n type n int rtype bool if n 1 return True def square each num 定义一个用于计算每一位数平方和的函数
  • Linux系统安装JDK1.8

    1 安装JDK 可以直接使用finallshell拖过去 统一放在linux中 usr soft目录下 1 解压该软件 tar zxvf jdk 8uXXXX tar gz 2 重命名解压后的目录 3 配置jdk的环境变量 再任何目录下都可
  • STM32学习笔记7——浮点数四舍五入

    C 中浮点转换为整型是截断的 直接将后面的小数去掉 而不是四舍五入 如 uint16 t 12 89 12 而不是13 项目中写了个小函数 将浮点数输入后 直接用7段译码管显示 用上述方法转换为整型后发现有显示误差 解决方法如下 1 定义一
  • 往Oracle数据库导入数据的两种方法

    在升级项目中 经常需要对数据进行迁移 我这次主要操作的是将数据从Access迁移到Oracle中 如何将数据导入Oracle数据库中 我总结了两种方法 供参考 1 SQL loader 1 1 主要特征 SQL loader是Oracle数
  • python图像分割模型_图像分割python

    常用的十大 python 图像处理工具 本文为 AI 研习社编译的技术博客 原标题 10 Python image manipulation tools 作者 Parul Pandey 翻译 安其罗 乔尔 JimmyHua 编辑 王立鱼 原
  • vue树形组件封装(移动端)

    最近在做移动端的项目 由于没有找见移动端树形组件 所以封装了一个 包含加载所有数据的功能以及懒加载功能 以下是目录结构 以下是完成后的ui 点击左侧切换 展开 收起 点击右侧其他操作 然后直接上代码 以下是懒加载的例子 一次性全部加载的就不
  • 一图看懂 pandas 模块(1):提供高性能、易用的数据结构和数据分析工具,资料整理+笔记(大全)

    本文由 大侠 AhcaoZhu 原创 转载请声明 链接 https blog csdn net Ahcao2008 一图看懂 pandas 模块 提供高性能 易用的数据结构和数据分析工具 资料整理 笔记 大全 摘要 模块图 类关系图 模块全
  • 【数据结构】单向链表的修改和删除

    单向链表的修改和删除 从单链表中删除一个节点思路 1 找到需要删除节点的前一个节点temp 2 temp next temp next next 3 被删除的节点 将不会有其他引用指向 会被垃圾处理机制回收 1 单向链表的修改操作 1 1
  • python轻量级web框架 flask

    文章目录 一 flask介绍 1 flask的构成 2 使用flask框架的原因 3 flask的优点 4 flask构成部分的介绍 5 flask特点 6 flask的基本模式 7 使用的flask版本 8 flask提供了什么 二 开始
  • 正方教务系统成绩爬虫的实现

    正方教务系统爬虫 简介 一 设计思路以及工具 二 实现步骤 1 登陆流程 1 1抓取登陆链接 1 2 验证码获取 1 3 发送登陆请求 2 读入数据 2 1 获取历年成绩对应的 VIEWSTATE 3 数据处理 3 1 存放数据 总结 简介
  • 子查询与JOIN&LEFT JOIN比较

    MySQL从4 1版本开始支持子查询 使用子查询进行SELECT语句嵌套查询 可以一次完成很多逻辑上需要多个步骤才能完成的SQL操作 子查询虽然很灵活 但是执行效率并不高 原因 执行子查询时 MySQL需要创建临时表 查询完毕后再删除这些临
  • vue2的了解

    目录 前言 一 性能优化 二 vue 1 keep live 2 vuex 3 v once 4 mixin 5 v if和v show 6 防抖和节流 7 promise 8 freez冻结数据 9 http状态码 10 重绘和回流 11
  • (Java)leetcode-76 Minimum Window Substring(最小覆盖子串)

    题目描述 给你一个字符串 s 一个字符串 t 返回 s 中涵盖 t 所有字符的最小子串 如果 s 中不存在涵盖 t 所有字符的子串 则返回空字符串 注意 如果 s 中存在这样的子串 我们保证它是唯一的答案 示例 1 输入 s ADOBECO