(滑动窗口) 76. 最小覆盖子串 ——【Leetcode每日一题】

news2024/12/24 17:33:51

❓76. 最小覆盖子串

难度:困难

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

注意:

  • 对于 t重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案
示例 1:

输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

示例 2:

输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示

  • m == s.length
  • n == t.length
  • 1 < = m , n < = 1 0 5 1 <= m, n <= 10^5 1<=m,n<=105
  • st 由英文字母组成

进阶:你能设计一个在 O ( m + n ) O(m+n) O(m+n) 时间 内解决此问题的算法吗?

💡思路:滑动窗口

滑动窗口的思想
lr 表示滑动窗口的左边界右边界,通过改变 l, r扩展收缩 滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串 t 的所有元素,记录下这个滑动窗口的长度 r - l +1,这些长度中的最小值就是要求的结果。

由于 t 可能包含 重复字符 ,所以使用哈希表 ori 记录 t 中所有的字符以及它们的个数;使用另一个哈希表 cnt 动态维护窗口中所有的字符以及它们的个数。

如果 当前窗口 中包含 t 中所有的字符,且对应的字符个数都不小于 t 的哈希表中各个字符的个数,则当前窗口是「可行」的。此时左边界 l 右移,直到不涵盖 t 中的所有字符,上一个即为满足条件的 当前窗口的最小子串

使用 distance 记录 当前窗口 中字符 属于 t 对应的个数;当 cnt[s[r]] < ori[s[r]] 时,每增加一个 s[r]distance ++ ,否则 distance 不变。 同理 当 cnt[s[l]] <= ori[s[l]] 每减少一个 s[l]distance -- ,否则 distance 不变。(这样可以在 O ( 1 ) O(1) O(1) 的复杂度判断 当前窗口 内的字符是否符合要求

具体步骤如下

  1. 不断增加 r 使滑动窗口 增大,直到窗口包含了 t 的所有元素;
  2. 不断增加 l 使滑动窗口 缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值;
  3. l 再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从 步骤 1 开始执行,寻找新的满足条件的滑动窗口,如此反复,直到 r 超出了字符串 s 范围。

🍁代码:(C++、Java)

C++

class Solution {
public:
    unordered_map <char, int> ori, cnt;

    string minWindow(string s, string t) {
        for(const auto &c : t){ //使用哈希表记录t中所有的字符,及其对应的个数
            ++ori[c];
        }
        int l = 0, r = -1;
        int len = INT_MAX, ansl = -1;
        int distance = 0;

        while(r < int(s.size())){
            if(ori.find(s[++r]) != ori.end() && ++cnt[s[r]] <= ori[s[r]] ){//对任何属于t的字符都记录
                distance++;//记录当前窗口的子串中属于t的字符的个数
            }
            while(distance == t.size() && l <= r){//包括所有字符,左指针右移
                if(r - l + 1 < len){//更新结果
                    len = r - l + 1;
                    ansl = l;
                }
                if(ori.find(s[l]) != ori.end() && --cnt[s[l]] < ori[s[l]]){
                    distance--;
                }
                ++l;
            }
        }
        return ansl == -1 ? string() : s.substr(ansl, len);
    }
};

Java

class Solution {
    Map<Character, Integer> ori = new HashMap<Character, Integer>();
    Map<Character, Integer> cnt = new HashMap<Character, Integer>();
    public String minWindow(String s, String t) {
        for(int i = 0; i < t.length(); i++){//使用哈希表记录t中所有的字符,及其对应的个数
            char c = t.charAt(i);
            ori.put(c, ori.getOrDefault(c, 0) + 1);
        }
        
        int l = 0, r = -1;
        int len = Integer.MAX_VALUE, ansl = -1;
        int distance = 0;
        while(++r < s.length()){
            char c = s.charAt(r);
            if(ori.containsKey(c)){//对任何属于t的字符都记录
                cnt.put(c, cnt.getOrDefault(c, 0) + 1);
                if(cnt.get(c) <= ori.get(c)){//记录当前窗口的子串中属于t的字符的个数
                    distance++;
                }
            }
            while(distance == t.length() && l <= r){
                if(r - l + 1 < len){//更新结果
                    len = r - l + 1;
                    ansl = l;
                }
                if(ori.containsKey(s.charAt(l))){
                    cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1);
                    if(cnt.get(s.charAt(l)) < ori.get(s.charAt(l))){
                        distance--;
                    }
                }
                ++l;
            }
        }
        return ansl == -1 ? "" : s.substring(ansl, ansl + len);
    }
}
🚀 运行结果:

在这里插入图片描述

🕔 复杂度分析:
  • 时间复杂度 O ( ∣ s ∣ + ∣ t ∣ ) O(∣s∣+∣t∣) O(s+t),最坏情况下左右指针对 s 的每个元素各遍历一遍,哈希表中对 s 中的每个元素各插入、删除一次,对 t 中的元素各插入一次。
  • 空间复杂度 O ( C ) O(C) O(C),这里用了两张哈希表作为辅助空间,每张哈希表最多不会存放超过字符集大小的键值对,我们设字符集大小为 C ,则渐进空间复杂度为 O ( C ) O(C) O(C)

题目来源:力扣。

放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我LeetCode主页 / CSDN—力扣专栏,每日更新!

注: 如有不足,欢迎指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1093687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言 sizeof

定义 sizeof是C语言的一种单目操作符。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。 使用方法 用于数据类型 sizeof(type) 数据类型必须用括号括住 用于变量 size…

C#,数值计算——数据建模Proposal的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Proposal { public Normaldev gau { get; set; } null; private double logstep { get; set; } public Proposal(int ranseed, double lstep) { this.gau…

OpenRemote: Java 开源 IoT 物联网开发平台,匹配智慧城市、智能家居、能源管理

OpenRemote 是一个直观、用户友好的基于Java语言的开源 IoT 物联网设备管理平台&#xff0c;它包括从连接设备到构建应用程序和特定领域的智能应用程序的所有功能和特性。通过OpenRemote物联网平台&#xff0c;用户可以收集和处理来自不同设备的传感器数据&#xff0c;适用于智…

办理400电话客服中心的申请步骤及注意事项

引言&#xff1a; 在现代商业环境中&#xff0c;提供优质的客户服务是企业成功的关键之一。而办理400电话客服中心可以帮助企业建立一个高效、专业的客户服务团队&#xff0c;提升客户满意度和忠诚度。本文将介绍办理400电话客服中心的申请步骤及注意事项&#xff0c;帮助企业顺…

java: 无效的目标发行版: 11

第一步&#xff1a; 第二步&#xff1a; 第三步

BGP初解笔记

BGP&#xff08;公网用得多&#xff09;&#xff1a; 一、名词&#xff1a; 1、BGP speaker&#xff1a;启用了BGP进程的路由器 2、BGP对等体&#xff1a;双方建立BGP邻居关系的设备&#xff1a; a.IBGP对等体&#xff0c;AS号一致&#xff0c;为IBGP对等体&#xff0c;有水…

如何做好商品的库存管理?哪些指标是衡量库存的指标

如何做好商品的库存管理&#xff1f;哪些指标是衡量库存的指标&#xff1f;库存分析的方法繁杂且广泛&#xff0c;选择正确的方法才能更好的进行库存分析。 本文将为大家盘点一些常用的库存分析方法和监控指标&#xff0c;全程干货&#xff0c;建议收藏&#xff01; 01 如何进…

网络编程中的重难点:套接字的应用和理解

什么是网络编程 网络编程&#xff0c;指的是网络上的主机&#xff0c;通过不同的进程&#xff0c;以编程的方式实现网络通信&#xff08;或成为网络数据传输&#xff09;。 发送端和接收端 在一次网络数据传输时&#xff1a; 发送端&#xff1a;数据的发送方进程&#xff0…

GD32F103 ADC

1. 模拟量于数字量。 模拟量&#xff1a;反应真实世界中的物理量&#xff08;比如温度&#xff0c;压力&#xff0c;长度&#xff09;模拟量通常是通过电压&#xff0c;电流等信号来表示。 数字量&#xff1a;通常是0和1来表示某个物理量的变化。 2. ADC&#xff08;模拟量转…

通过HTTP发送大量数据的三种方法

在网络的早期时期&#xff0c;人们发送的文件大小仅为几KB。到了2023年&#xff0c;我们享受着高分辨率的MB级别图像&#xff0c;并在几GB的4K&#xff08;即将是8K&#xff09;视频中观看。 即使有良好的互联网连接&#xff0c;下载一个5GB的文件仍然需要一些时间。如果你拥有…

Photoshop 2024正式发布!内置最新PS AI,创意填充等功能无限制使用!

PS正式版目前更新到了2024&#xff0c;版本为25.0。 安装教程 1、下载得到安装包后&#xff0c;先解压。鼠标右键&#xff0c;【解压到当前文件夹】 2、双击 Set-up 开始安装 3、这里可以更改安装位置。如果C盘空间不够大&#xff0c;可以把它安装到C盘以外。更改好后&#x…

SpringBoot面试题3:Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的? Spring Boot 的核心注解是 @SpringBootApplication。 @SpringBootApplication 是一…

【idea】 java: 找不到符号

idea 启动时提示 java: 找不到符号 java: 找不到符号 符号: 方法 getCompanyDisputeCount() 位置: 类型为com.yang.entity.AreaAnalyse的变量 areaAnalyse 在setting ——> Compiler ——>Shared build process VM options: 添加&#xff1a; -Djps.track.ap.dep…

Vue3<script setup>语法糖下,实现父子组件通信以及数据监听的三种方法。

在Vue3的script setup语法糖中&#xff0c;没有办法通过Vue2的ref、props、parent、中央时间总线等等众多方法&#xff0c;通过this指针简单的实现父子组件的通信&#xff0c;网络上也很少有关于script setup语法糖的相关教程&#xff0c;所以决定自己写一个详细教程&#xff0…

【算法|动态规划No.19】leetcode413. 等差数列划分

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

JAVA反序列化漏洞

JAVA反序列化漏洞 原文资料&#xff1a;xiu–》xiu博客 文章目录 JAVA反序列化漏洞idea类继承反序列化漏洞person类Test类 什么是反序列化漏洞 idea 类继承 public class Person {public int age;public String name;public void talk(){System.out.println("Person 说话…

RK3588 USB WIFI调试

一.安卓wifi框架 要使用一个wifi功能需要涉及的部分有内核部分wifi驱动&#xff0c;应用部分wpa_supplicant服务。其中wifi驱动又包含很多部分&#xff0c;分为通讯接口的驱动SDIO、USB、PCIE等&#xff0c;还有上下电部分的驱动&#xff0c;wifi模组提供部分的驱动。应用部分不…

random生成随机数的灵活运用

random返回的 [0,1) 之间的一个随即小数 思考&#xff1a;请写出获取 a-b 之间的一个随机整数&#xff0c;a,b均为整数&#xff0c;比如 a2 , b7 即返回一个数 x > [2,7]Math.random()*(b-a) 返回的就是 [0,b-a](int)(aMath.random()*(b-a1)) 》 (int)(2Math.random()*6) Ma…

常用傅里叶变换表

傅里叶展开 傅里叶变换 傅里叶逆变换 时域信号 弧频域信号 线性变换 时域平移 频域平移 伸缩变换 微分性质 逆变换的微分性质 卷积定理 原函数变换结果 单位阶跃函数&#xff1a; 符号函数&#xff1a; 矩形函数&#xff1a; 辛格函数&#xff1a;

系统架构师备考倒计时22天(每日知识点)

测试阶段划分 单元测试&#xff1a;依据详细设计&#xff0c;模块测试&#xff0c;模块功能、性能、接口等集成测试&#xff1a;依据概要设计&#xff0c;模块间的接口系统测试&#xff1a;依据需求文档&#xff0c;在真实环境下&#xff0c;验证完整的软件配置项能否和系统正…