力扣最热一百题——最小覆盖子串

目录

题目链接:76. 最小覆盖子串 - 力扣(LeetCode)

题目描述

示例

提示:

解法一:滑动窗口

1. 初始化

2. 构建 mapT

3. 滑动窗口

4. checkT 方法

5. 返回结果

Java写法:

运行时间

C++写法:

相比于Java主要改动:

运行时间

时间复杂度和空间复杂度

解法一极限优化

优化说明

运行时间 

总结


题目链接:76. 最小覆盖子串 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

给你一个字符串 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 <= 10^5
  • s 和 t 由英文字母组成


解法一:滑动窗口

1. 初始化

  • 使用两个 HashMapmapS 和 mapT)来记录字符出现的次数。mapT 用于存储字符串 t 中每个字符的出现次数,而 mapS 用于在滑动窗口过程中动态地记录当前窗口内字符的出现次数。
  • 初始化结果子串的起始位置 resStart 为 -1(表示还未找到符合条件的子串),和结果子串的长度 resLen 为 s 的长度加1(确保初始时,任何可能的子串长度都会比这个值小,从而能够更新)。

2. 构建 mapT

  • 遍历字符串 t,统计每个字符的出现次数,并存储在 mapT 中。

3. 滑动窗口

  • 使用两个指针 left 和 right 来表示当前滑动窗口的左右边界。
  • 使用一个 while 循环来移动这两个指针,直到 right 指针超出字符串 s 的范围或当前窗口已经包含了 t 的所有字符(通过 checkT 方法检查)。
  • 在循环内部,首先检查是否需要向右扩展窗口(即移动 right 指针)。如果当前窗口不包含 t 的所有字符(!checkT(mapS, mapT) 为真),则扩展窗口(即将 s.charAt(right) 加入到 mapS 中,并右移 right 指针)。
  • 如果当前窗口已经包含了 t 的所有字符,则尝试通过左移 left 指针来缩小窗口,直到窗口不再包含 t 的所有字符为止。在每次缩小窗口的过程中,都检查并更新结果子串的起始位置和长度(如果当前窗口长度更小的话)。

4. checkT 方法

  • 这个方法用于检查 mapS 是否包含了 mapT 中的所有字符,并且每个字符的出现次数都不少于 mapT 中对应字符的出现次数。
  • 遍历 mapT 中的每个字符,检查其在 mapS 中的出现次数是否满足条件。如果有任何一个字符不满足条件,则返回 false;否则返回 true

5. 返回结果

  • 循环结束后,如果找到了符合条件的子串(即 resStart 不为 -1),则返回该子串;否则返回空字符串 "",表示没有找到符合条件的子串。

Java写法:

class Solution {public String minWindow(String s, String t) {int lenS = s.length();Map<Character,Integer> mapS = new HashMap<>();Map<Character,Integer> mapT = new HashMap<>();int resStart = -1;int resLen = lenS + 1;// 将t中的元素使用map集合存储起来for (int i = 0; i < t.length(); i++) {mapT.put(t.charAt(i),mapT.getOrDefault(t.charAt(i),0) + 1);}int left = 0;int right = 0;while (right < lenS || checkT(mapS, mapT)){if (right > lenS || !checkT(mapS, mapT)){// 如果当前窗口没有包含的话就添加元素,并右移rightmapS.put(s.charAt(right), mapS.getOrDefault(s.charAt(right),0) + 1);right++;}else {// 如果当前窗口包含了的话,判断并更新结果的起始位置和长度if (resLen > (right - left)){resStart = left;resLen = right - left;}// 就删除元素,并右移leftmapS.put(s.charAt(left), mapS.get(s.charAt(left)) - 1);left++;}}if(resStart != -1){return s.substring(resStart, resStart + resLen);}return "";}/*** 检查mapS是否包含了mapT中的全部字符* @return*/private boolean checkT(Map<Character, Integer> mapS, Map<Character, Integer> mapT) {for (Character character : mapT.keySet()) {if (mapT.get(character) > mapS.getOrDefault(character, 0)){return false;}}return true;}
}

运行时间

520送给大家

 

C++写法:


class Solution {
public:std::string minWindow(std::string s, std::string t) {int lenS = s.length();int lenT = t.length();std::unordered_map<char, int> mapS;std::unordered_map<char, int> mapT;// 将t中的元素使用map集合存储起来for (char c : t) {mapT[c]++;}int resStart = -1;int resLen = lenS + 1;int left = 0, right = 0;int required = mapT.size(); // 需要的不同字符数量int formed = 0; // 当前窗口中满足条件的不同字符数量while (right < lenS) {// 添加右指针的字符char c = s[right];mapS[c]++;// 如果当前字符在t中并且频率达标,则形成一个满足条件的字符if (mapT.count(c) && mapS[c] == mapT[c]) {formed++;}// 当当前窗口中包含了所有t的字符时,尝试收缩左指针while (left <= right && formed == required) {c = s[left];// 更新最小子串的长度和位置if (resLen > (right - left + 1)) {resStart = left;resLen = right - left + 1;}// 移除左指针指向的字符,并更新窗口mapS[c]--;if (mapT.count(c) && mapS[c] < mapT[c]) {formed--;}left++;}right++;}// 如果没有找到符合条件的子串,则返回空字符串,否则返回最小子串return resStart != -1 ? s.substr(resStart, resLen) : "";}
};

相比于Java主要改动:

  1. 数据结构:Java 的 HashMap 改为 C++ 的 unordered_map
  2. 字符串处理:Java 的字符串操作改为 C++ 的 string 方法,例如 s.substr
  3. 类型和语法:调整了类型和语法,使其符合 C++ 规范。
  4. 字符计数:在删除字符时,如果计数为 0,使用 erase 方法删除该字符。
  5. 主循环条件:适当调整了 while 循环的条件,以确保在右指针达到字符串末尾时不会越界。

 

运行时间

时间复杂度和空间复杂度


解法一极限优化

我就只写Java了,脑子要炸掉了

优化说明

  1. 窗口逻辑简化:原代码在主循环中条件判断较复杂,优化后将扩展右指针和收缩左指针的逻辑分开,使代码结构更清晰。

  2. 减少 mapS 的使用:在添加和移除字符时,直接更新 mapS 的计数,而不是每次都调用 getOrDefault,提高了性能。

  3. 优化 formed 计数:通过使用 formed 变量来跟踪当前满足条件的字符数量,避免每次调用检查 checkT 方法,减少了不必要的遍历。

  4. 使用数组来存储结果:使用 int[] result 数组来存储最小长度和指针位置,使代码更简洁,易于理解。

class Solution {public String minWindow(String s, String t) {int lenS = s.length(), lenT = t.length();// 如果s的长度小于t的长度,则直接返回空字符串if (lenS < lenT) return "";// 创建mapT,存储t中每个字符的频率Map<Character, Integer> mapT = new HashMap<>();for (char c : t.toCharArray()) {mapT.put(c, mapT.getOrDefault(c, 0) + 1);}// 创建mapS,存储当前窗口s中每个字符的频率Map<Character, Integer> mapS = new HashMap<>();int left = 0, right = 0;int required = mapT.size(); // 需要的不同字符数量int formed = 0; // 当前窗口中满足条件的不同字符数量int[] result = {-1, 0, 0}; // result[0]: 最小长度, result[1]: 左指针位置, result[2]: 右指针位置// 扩展右指针,形成窗口while (right < lenS) {char c = s.charAt(right);mapS.put(c, mapS.getOrDefault(c, 0) + 1);// 如果当前字符在t中,并且频率达到要求,则形成一个满足条件的字符if (mapT.containsKey(c) && mapS.get(c).intValue() == mapT.get(c).intValue()) {formed++;}// 当当前窗口中包含了所有t的字符时,尝试收缩左指针while (left <= right && formed == required) {c = s.charAt(left);// 更新最小子串的长度和位置if (result[0] == -1 || right - left + 1 < result[0]) {result[0] = right - left + 1;result[1] = left;result[2] = right;}// 移除左指针指向的字符,并更新窗口mapS.put(c, mapS.get(c) - 1);// 如果移除后该字符不再满足条件,则减少formed计数if (mapT.containsKey(c) && mapS.get(c).intValue() < mapT.get(c).intValue()) {formed--;}left++;}right++;}// 如果没有找到符合条件的子串,则返回空字符串,否则返回最小子串return result[0] == -1 ? "" : s.substring(result[1], result[2] + 1);}
}

运行时间 

        这优化效果,牛不牛!!!!!


总结

        我真的脑子爆炸了,┭┮﹏┭┮晚安

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

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

相关文章

人工智能与自然语言处理发展史

前言 在科技的浪潮中&#xff0c;人工智能 (AI) 作为一股不可阻挡的力量&#xff0c;持续推动着社会与科技的进步。本博客旨在深入剖析人工智能及其核心领域——神经网络、自然语言处理、统计语言模型、以及大规模语言模型——的演进历程&#xff0c;以专业的视角展现这一领域…

基于C语言开发(控制台)通讯录管理程序

通讯录程序设计 一、课程设计题目与要求 题目 &#xff1a;通讯录管理程序 1. 问题描述 编写一个简单的通讯录管理程序。通讯录记录有姓名&#xff0c;地址(省、市(县)、街道)&#xff0c;电话号码&#xff0c;邮政编码等四项。2. 基本要求 程序应提供的基本基本管理功能有…

豆包 MarsCode 代码练习体验

我最近体验了豆包MarsCode的代码练习&#xff0c;感觉非常棒&#xff01;首先&#xff0c;进入平台后&#xff0c;界面简洁明了&#xff0c;使用起来非常方便。选择内置题目时&#xff0c;题目类型丰富多样&#xff0c;涵盖了基础知识和一些进阶挑战&#xff0c;非常适合不同水…

【Kubernetes知识点】解读HPA的 thrashing(抖动)问题

【Kubernetes知识点】解读HPA的 thrashing&#xff08;抖动&#xff09;问题 目录 1 概念 1.1 什么是 Thrashing 现象&#xff1f;1.2 HPA 中 Thrashing 产生的原因1.3 解决 Thrashing 的优化措施 1.3.1 设置合适的阈值1.3.2 使用自定义指标和基于负载的自动扩缩1.3.3 增加扩…

探寻大模型时代智慧农业新未来,商汤与上海市农委达成战略合作

近日&#xff0c;在中国农民丰收节上海会场丰收庆典活动上&#xff0c;商汤科技与上海市农业农村委员会&#xff08;下称&#xff1a;上海市农委&#xff09;签署战略合作协议&#xff0c;双方将依托先进的AI大模型技术&#xff0c;共同推进上海智慧农业发展&#xff0c;打造国…

基向量和投影矩阵

文章目录 1. 投影向量2. 基向量&#xff0c;列向量秩1分解3. SVD&#xff0c;奇异向量秩1分解4. 小结&#xff1a;5. 图解分析 1. 投影向量 假设我们有一个向量b和一个向量q,求向量b在向量q上的投影向量p: 求向量p的长度&#xff1a; q T b ∣ q ∣ ⋅ ∣ b ∣ ⋅ cos ⁡ …

UNet 眼底血管分割实战教程

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 在医学影像分析领域&#xff0c;准确地分割眼底血管对于眼科疾病的诊断和治疗至关重要。…

[产品管理-33]:实验室技术与商业化产品的距离,实验室技术在商业化过程中要越过多少道“坎”?

目录 一、实验室技术 1.1 实验室研究性技术 1.2 技术发展的S曲线 技术发展S曲线的主要阶段和特点 技术发展S曲线的意义和应用 二、实验室技术商业化的路径 2.1 实验室技术与商业化产品的距离 1、技术成熟度与稳定性 - 技术自身 2、市场需求与适应性 - 技术是满足需求 …

关于yolov5训练需要更改的参数汇总

首先我给大家展示一下项目目录 第一步我们需要修改data文件夹下的voc.yaml文件&#xff0c;这里我复制了一份改名为hat.yaml 需要修改第21&#xff0c;22行的路径&#xff0c;train是图片的训练集&#xff0c;val是图片训练的验证集&#xff0c;nc是标签的数量&#xff0c;name…

解决银河麒麟桌面操作系统V10SP1 SSH连接“connection reset by ip地址 port 22”问题

解决银河麒麟桌面操作系统V10SP1 SSH连接“connection reset by ip地址 port 22”问题 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 遇到SSH连接银河麒麟V10SP1时“connection reset by ip地址 port 22”的错误&#xff0c;可以尝试以下步…

深入浅出热门AI大模型,新手到专家的必备指南《实战AI大模型》

今天&#xff0c;人工智能技术的快速发展和广泛应用已经引起了大众的关注和兴趣&#xff0c;它不仅成为技术发展的核心驱动力&#xff0c;更是推动着社会生活的全方位变革。特别是作为AI重要分支的深度学习&#xff0c;通过不断刷新的表现力已引领并定义了一场科技革命。大型深…

矿区车辆4G视频监控解决方案

一、背景介绍 随着科技的发展和矿山产业的不断进步&#xff0c;矿区的安全问题越来越受到关注。尤其是矿区车辆的运行安全&#xff0c;更是重中之重。为了更好地对矿区车辆进行监控和管理&#xff0c;提高运行安全性&#xff0c;4G视频监控解决方案应运而生。 二、需求分析 1…

Nmap网络扫描器基础功能介绍

怎么快速知道网络中存在哪些设备呢&#xff1f;我们可以借用扫描工具Nmap来实现这个功能。 下载 Windows系统可以前往Nmap官网下载安装包。 Linux使用对应的包管理器可以直接安装&#xff0c;命令如下 # Debian/Ubuntu apt install nmap# RedHat/Fedora yum install nmap …

全西安前十的数字媒体产业链都在这

在古城西安&#xff0c;有一处汇聚着创新与活力的地方&#xff0c;那便是西安国际数字影像产业园。这里&#xff0c;承载着西安数字媒体产业的未来与希望&#xff0c;成为了数字媒体产业链的闪耀聚集地。 西安国际数字影像产业园以其独特的魅力和优势&#xff0c;吸引了众多数字…

Go语言基础学习01-Liunx下Go开发环境配置;源码组织方式;go build/install/get详解

目录 Linux环境下配置安装VScode并配置Go语言开发环境Go语言源码的组织方式Go语言源码安装后的结果Go程序构建和安装的过程go build扩展go get 命令详解 之前学习过Go语言&#xff0c;学习的时候没有记录笔记&#xff0c;最近找了个极客时间的Go语言36讲&#xff0c;打算时间学…

004_动手实现MLP(pytorch)

import torch from torch import nn from torch.nn import init import numpy as np import sys import d2lzh_pytorch as d2l # 1.数据预处理 mnist_train torchvision.datasets.FashionMNIST(root/Users/w/PycharmProjects/DeepLearning_with_LiMu/datasets/FashionMnist, t…

二刷LeetCode:“51.N皇后 37.解数独”题解心得(简单易懂)

引言&#xff08;初遇噩梦&#xff0c;再遇坦然&#xff09; 在阅读本文之前&#xff0c;建议大家已经接触过回溯算法&#xff0c;并完成回溯相关题目&#xff0c;例如&#xff1a;子集问题、组合问题、排列问题。 子集&#xff1a;子集II、子集 组合&#xff1a;组合、组合总和…

多比特AI事业部VP程伟光受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 武汉市多比特信息科技有限公司AI事业部VP程伟光先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“AI对于项目经理工作的影响和变化解析”。大会将于10月26-27日在北京举办&am…

Scanner流程控制语句

1. Scanner类 Scanner的意思是扫描 Scanner是JDK提供的一个类&#xff0c;位于java.util包下&#xff0c;所以我们如果需要使用则必须导包&#xff0c;导包的语句必须在声明包之后&#xff0c;在声明类之前 Scanner类是用来接受用户输入的各种信息 Scanner类提供了用于接受…

SpringBoot开发——整合Hutool工具类轻松生成验证码

文章目录 1、Hutool简介2、验证码效果展示2.1 扭曲干扰验证码2.2 线条干扰验证码2.3 圆圈干扰验证码3、验证码应用场景3.1. 用户注册与身份验证3.2. 支付验证3.3. 订单与物流通知3.4. 信息安全与隐私保护3.5. 通知与提醒3.6. 其他应用场景4、Hutool工具类实现验证码生成4.1 引入…