算法leetcode|76. 最小覆盖子串(rust重拳出击)


文章目录

  • 76. 最小覆盖子串:
    • 样例 1:
    • 样例 2:
    • 样例 3:
    • 提示:
    • 进阶:
  • 分析:
    • 在这里插入图片描述
  • 题解:
    • rust:
    • go:
    • c++:
    • python:
    • java:


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 <= 105
  • st 由英文字母组成

进阶:

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


分析:

  • 面对这道算法题目,二当家的再次陷入了沉思。
  • 直接采用双层循环暴力解决的话,时间复杂度会比较高,恐怕过不了用例,没有去试。
  • 题目并不要求字符的顺序,只要求了每种字符的数量,那首先就想到要做计数。
  • 接下来考虑如何降低时间复杂度,每次循环都从新匹配子串是低效的,要尽可能复用,滑动窗口在这里非常合适,使用双指针,先直接移动右指针找到第一个满足条件的子串,记下长度作为备选结果,接下来移动左指针,当不满足覆盖子串的条件时就继续移动右置针直到满足覆盖子串d饿条件,并比较子串的长度,取较小的子串长度作为备选结果,重复该操作,直到循环遍历完毕。
  • 如何高效判断遍历的子串已经满足覆盖子串呢?字符计数这时候就要大展拳脚了,先在遍历字符串 s 之前,先对字符串 t 进行一次遍历,做初始化计数,记录下每种字符的个数(题解中这里使用了减法,使用加法也是可以的,只是后面的加减法就要变),在遍历 s 时,移动右指针就是延长了覆盖子串,同时修改计数,这里的加减法计算要和前面初始化的相反,判断计数是否为0,如果变为0则表示,这种字符的个数已经没有差异了,但是我们需要覆盖了 t 中的每一种字符,所以需要判断 26 个字符的个数是不是都够了,如果都够了,就是满足了覆盖子串,接下来就移动左指针,同时修改计数,这里的加减计算要和右指针移动的相反。
  • 当某个字符的计数变为0时,我们需要判断 26 种字符的字符个数是不是都满足了,这里就需要26次循环,是否有更高效的办法呢?我们可以额外增加一个变量记录有差异的字符种类数(记录有差异的字符数也是可以的,但是后面的逻辑会有一点区别,思想大致相同), 初始化时顺便初始化该变量,在遍历匹配中,每当有字符计数变为0,就修改这个变量,如果这个变量变为0则表示完全覆盖,从而提高效率。
  • 只看文字可能不便理解,建议对照着熟悉的语言的题解一起看,希望可以有助学习理解。

在这里插入图片描述

题解:

rust:

impl Solution {pub fn min_window(s: String, t: String) -> String {// 少于目标字符串中数量的字符数量let mut diff_char_count = 0;// 字符计数器let mut cnt = vec![0; 128];// 初始化t.as_bytes().iter().for_each(|&b| {// 计数减少cnt[b as usize] -= 1;if cnt[b as usize] == -1 {// 差异字符数增加diff_char_count += 1;}});// 覆盖子串结果信息let (mut ans_len, mut ans_l) = (usize::MAX, usize::MAX);// 开始滑动窗口let s_len = s.len();let (mut l, mut r) = (0, 0);while r < s_len {// 计数增加cnt[s.as_bytes()[r] as usize] += 1;// 向右移动右边界后,可能该字符数量没有差异了if cnt[s.as_bytes()[r] as usize] == 0 {// 差异字符数减少diff_char_count -= 1;// 差异字符数减少后可能为0了if diff_char_count == 0 {// 向右滑动左边界,直到会有差异,取满足要求的最小串while cnt[s.as_bytes()[l] as usize] > 0 {cnt[s.as_bytes()[l] as usize] -= 1;l += 1;}// 更新结果if r - l + 1 < ans_len {ans_len = r - l + 1;ans_l = l;}// 向右移动左边界,差异字符数增加cnt[s.as_bytes()[l] as usize] -= 1;l += 1;diff_char_count += 1;}}r += 1;}return if ans_l == usize::MAX {"".to_string()} else {s[ans_l..ans_l + ans_len].to_string()}}
}

go:

func minWindow(s string, t string) string {// 少于目标字符串中数量的字符数量diffCharCount := 0// 字符计数器cnt := make([]int, 128)// 初始化for _, c := range t {// 计数减少cnt[c]--if cnt[c] == -1 {// 差异字符数增加diffCharCount++}}// 覆盖子串结果信息ansLen, ansL := math.MaxInt32, -1// 开始滑动窗口sLen := len(s)l, r := 0, 0for r < sLen {// 计数增加cnt[s[r]]++// 向右移动右边界后,可能该字符数量没有差异了if cnt[s[r]] == 0 {// 差异字符数减少diffCharCount--// 差异字符数减少后可能为0了if diffCharCount == 0 {// 向右滑动左边界,直到会有差异,取满足要求的最小串for cnt[s[l]] > 0 {cnt[s[l]]--l++}// 更新结果if r-l+1 < ansLen {ansLen = r - l + 1ansL = l}// 向右移动左边界,差异字符数增加cnt[s[l]]--l++diffCharCount++}}r++}if ansL == -1 {return ""} else {return s[ansL : ansL+ansLen]}
}

c++:

class Solution {
public:string minWindow(string s, string t) {// 少于目标字符串中数量的字符数量int diffCharCount = 0;// 字符计数器int cnt[128];memset(cnt, 0, sizeof(cnt));// 初始化const int tLen = t.length();for (int i = 0; i < tLen; ++i) {if (--cnt[t[i]] == -1) {// 差异字符数增加++diffCharCount;}}// 覆盖子串结果信息int ansLen = INT_MAX, ansL = -1;// 开始滑动窗口const int sLen = s.length();int l = 0, r = -1;while (++r < sLen) {// 向右移动右边界后,可能该字符数量没有差异了if (++cnt[s[r]] == 0) {// 差异字符数减少后可能为0了if (--diffCharCount == 0) {// 向右滑动左边界,直到会有差异,取满足要求的最小串while (--cnt[s[l++]] >= 0) {// nothing}// 差异字符数增加++diffCharCount;// 更新结果if (r - l + 2 < ansLen) {ansLen = r - l + 2;ansL = l - 1;}}}}return ansL == -1 ? "" : s.substr(ansL, ansLen);}
};

python:

class Solution:def minWindow(self, s: str, t: str) -> str:# 少于目标字符串中数量的字符数量diff_char_count = 0# 字符计数器cnt = collections.defaultdict(int)# 初始化for c in t:# 计数减少cnt[c] -= 1if cnt[c] == -1:# 差异字符数增加diff_char_count += 1# 覆盖子串结果信息ans_len, ans_l = float("inf"), -1# 开始滑动窗口s_len = len(s)l, r = 0, 0while r < s_len:# 计数增加cnt[s[r]] += 1# 向右移动右边界后,可能该字符数量没有差异了if cnt[s[r]] == 0:# 差异字符数减少diff_char_count -= 1# 差异字符数减少后可能为0了if diff_char_count == 0:# 向右滑动左边界,直到会有差异,取满足要求的最小串while cnt[s[l]] > 0:cnt[s[l]] -= 1l += 1# 更新结果if r - l + 1 < ans_len:ans_len = r - l + 1ans_l = l# 向右移动左边界,差异字符数增加cnt[s[l]] -= 1l += 1diff_char_count += 1r += 1return "" if ans_l == -1 else s[ans_l: ans_l + ans_len]

java:

class Solution {public String minWindow(String s, String t) {// 少于目标字符串中数量的字符数量int diffCharCount = 0;// 字符计数器final int[] cnt = new int[128];// 初始化final int tLen = t.length();for (int i = 0; i < tLen; ++i) {if (--cnt[t.charAt(i)] == -1) {// 差异字符数增加++diffCharCount;}}// 覆盖子串结果信息int ansLen = Integer.MAX_VALUE, ansL = -1;// 开始滑动窗口final int sLen = s.length();int       l    = 0, r = -1;while (++r < sLen) {// 向右移动右边界后,可能该字符数量没有差异了if (++cnt[s.charAt(r)] == 0) {// 差异字符数减少后可能为0了if (--diffCharCount == 0) {// 向右滑动左边界,直到会有差异,取满足要求的最小串while (--cnt[s.charAt(l++)] >= 0) {// nothing}// 差异字符数增加++diffCharCount;// 更新结果if (r - l + 2 < ansLen) {ansLen = r - l + 2;ansL = l - 1;}}}}return ansL == -1 ? "" : s.substring(ansL, ansL + ansLen);}
}

非常感谢你阅读本文~
欢迎【点赞】【收藏】【评论】三连走一波~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://le-yi.blog.csdn.net/ 博客原创~


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

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

相关文章

若依新建模块

下面介绍如何在若依框架下新建一个子模块 第一步&#xff1a; 如图操作&#xff1a; 1. 2. 3. 4.在刚建立的子模块的pom.xml文件添加通用工具依赖 代码&#xff1a; <dependencies> <!-- 导入通用工具--><dependency><groupId>com.rchuing&l…

2023-09-04 Linux 让shell编译脚本里面设置的环境变量改变kernel里面驱动文件的宏定义值方法,我这里用来做修改固件版本

一、原生的读取版本接口是/proc/version&#xff0c;我这里需要提供获取固件版本号的api给app&#xff0c;因为版本号会经常需要修改&#xff0c;如果每次都到kernel下修改比较麻烦&#xff0c;我这里是想在编译脚本里面对版本号进行修改&#xff0c;这样方便一点。 二、主要修…

无需租云服务器,Linux本地搭建web服务,并内网穿透发布公网访问

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…

2023 最新前端面试题 (HTML 篇)

1. src 和 href 的区别 src 用于替换当前元素&#xff08;引入&#xff09;&#xff0c;href 用于在当前文档和引用资源之间确立联系&#xff08;引用&#xff09; &#xff08;1&#xff09;src&#xff08;source&#xff09; 指向外部资源的位置&#xff0c;指向的内容将会嵌…

安卓 MeasureCache优化了什么?

安卓绘制原理概览_油炸板蓝根的博客-CSDN博客 搜了一下&#xff0c;全网居然没有人提过 measureCache。 在前文中提到过&#xff0c;measure的时候&#xff0c;如果命中了 measureCache&#xff0c;会跳过 onMeasure&#xff0c;同时会设置 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOU…

TypeScript_树结构-BST树

树结构 树的特点 树通常有一个根。连接着根的是树干树干到上面之后会进行分叉成树枝&#xff0c;树枝还会分又成更小的树枝在树枝的最后是叶子 树的抽象 树可以模拟生活中的很多场景&#xff0c;比如&#xff1a;公司组织架构、家谱、DOM Tree、电脑文件夹架构 优秀的哈希函…

【Unity】VS Code 没有自动补全 MonoBehaviour 的方法

正常来说&#xff0c;在VS Code 输入类似 OnTriggerEnter2D等方法名时&#xff0c;VS Code会根据已经输入的前缀自动提示相关方法。 在不正常的情况下&#xff0c;根据StackOverFlow上面的回答&#xff0c;依次试过了 安装 .NET SDK安装 .NET Framework Dev PackVS Code安装 …

openGauss学习笔记-60 openGauss 数据库管理-逻辑存储结构

文章目录 openGauss学习笔记-60 openGauss 数据库管理-逻辑存储结构 openGauss学习笔记-60 openGauss 数据库管理-逻辑存储结构 openGauss的数据库节点负责存储数据&#xff0c;其存储介质也是磁盘&#xff0c;本节主要从逻辑视角介绍数据库节点都有哪些对象&#xff0c;以及这…

深入解析Kotlin类与对象:构造、伴生、单例全面剖析

前言 本篇文章将带您了解Kotlin编程中的重要概念&#xff1a;类及构造函数、访问修饰符、伴生对象和单例模式。就像搭积木一样&#xff0c;我们会逐步揭开这些概念的面纱&#xff0c;让您轻松理解它们的作用和用法。无论您是编程新手还是有经验的开发者&#xff0c;本文都将为…

Linux命令200例:man用于显示和阅读关于Linux内置命令的使用说明

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

后端SpringBoot+前端Vue前后端分离的项目(一)

前言&#xff1a;后端使用SpringBoot框架&#xff0c;前端使用Vue框架&#xff0c;做一个前后端分离的小项目&#xff0c;需求&#xff1a;实现一个表格&#xff0c;具备新增、删除、修改的功能。 目录 一、数据库表的设计 二、后端实现 环境配置 数据处理-增删改查 model…

Excel·VBA二维数组组合函数的应用实例

看到一个问题《关于#穷举#的问题&#xff0c;如何解决&#xff1f;(语言-开发语言)》&#xff0c;对同一个数据存在“是/否”2种状态&#xff0c;判断其是否参与计算&#xff0c;并输出一系列数据的“是/否”状态的结果 目录 方法1&#xff1a;二维数组组合函数结果 方法2&am…

【FusionInsight 迁移】HBase从C50迁移到6.5.1(02)C50上准备FTP Server

【FusionInsight 迁移】HBase从C50迁移到6.5.1&#xff08;02&#xff09;C50上准备FTP Server HBase从C50迁移到6.5.1&#xff08;02&#xff09;C50上准备FTP Server登录老集群FusionInsight C50的Manager准备FTP User准备FTP Server HBase从C50迁移到6.5.1&#xff08;02&am…

Java泛型(待补充)

泛型是一种“代码模板”&#xff0c;可以用一套代码套用各种类型。 一、什么是泛型&#xff1f; 泛型就是编写模板代码来适应任意类型&#xff1b;泛型的好处是使用时不必对类型进行强制转换&#xff0c;它通过编译器对类型进行检查&#xff1b;注意泛型的继承关系&#xff1a…

Vue3响应式源码实现

Vue3响应式源码实现 初始化项目结构 vue-proxy ├── effect.js ├── effect.ts ├── index.html ├── index.js ├── package.json ├── reactive.js ├── reactive.ts └── webpack.config.jsreactive.ts import { track, trigger } from "./effect&q…

java操作adb查看apk安装包包名【搬代码】

Testpublic static void findadb() throws InterruptedException {String apkip"E:\\需求\\2023\\gql_1.0.1.apk";String findname1"cmd /c cd E:\\appium\\android-sdk\\build-tools\\27.0.2";//没有进到这里String s1 Cmd.exeCmd(findname1);System.out…

缓存案例-架构真题(二十二)

试题一 某大型电商平台建立一个B2B商店系统&#xff0c;并在全国建设了仓储中心。但是在运营过程中&#xff0c;发现很多跨仓储中心调货&#xff0c;延误运送。为此建立全国仓储系统&#xff0c;通过对订单的分析和挖掘&#xff0c;并通过大数据分析预测各类配置&#xff0c;降…

什么是接口测试,如何做接口测试?

比起点点点的功能测试&#xff0c;“接口测试”显得专业又高大上&#xff0c;也因此让有些初级测试人员“望而生畏”。别担心&#xff0c;其实接口测试也是功能测试的一种&#xff0c;它是针对接口进行的功能测试。 写在前面&#xff1a;本文参考了茹炳晟老师的《测试工程师 全…

opencv-4.5.2-android-sdk.zip安装教程

opencv-4.5.2-android-sdk.zip&#xff1a; 下载链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;s0p2 导入模块的方法: ①、导入模块 ②、定位到sdk目录 点击ok就行&#xff0c;就导入成功了。导入成功后会多出一个可展开的opencv文件夹(自己命名的),一定要能展…

Mediasoup在node.js下多线程实现

mediasoup基于socket.io的交互消息来完成join-room的请求过程。Join的过程&#xff0c;实际就是获取stream的过程&#xff0c;也就是视频加载时间(video-load-speed)。在RTMP系统&#xff0c;视频加载时间是秒开。Mediasoup给出的第一个frame是I-frame&#xff0c;但由于交互的…