力扣经典题目解析--最小覆盖子串

原题地址: . - 力扣(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 的子串中,
因此没有符合条件的子字符串,返回空字符串。

暴力法

暴力法就是遍历所有子串,然后统计子串中每个字符出现的次数,然后跟t中每个字符出现的次数做比较,如果包含t中所有字符并且字符出现的次数大于等于t,则说明该子串符合条件,找出所有满足条件的子串,通过比较得到最小子串

public String minWindow(String s, String t) {// 保存自小子串String result = "";// 统计t中字符串数量Map<Character, Integer> tCharStatis = statis(t);// 遍历sfor (int i = 0; i < s.length(); i++) {// 遍历子串,子串范围为[i,j)for (int j = i + t.length(); j <= s.length(); j++) {String subStr = s.substring(i, j);Map<Character, Integer> subCharStatis = statis(subStr);// 如果子串包含t,则判断是否为最小子串if (check(subCharStatis, tCharStatis)) {if ("".equals(result) || subStr.length() < result.length()) {result = subStr;}}}}return result;
}// 统计字符串中单个字符数量
private Map<Character, Integer> statis(String str) {Map<Character, Integer> map = new HashMap<>();for (int i = 0; i < str.length(); i++) {int count = map.getOrDefault(str.charAt(i), 0);count++;map.put(str.charAt(i), count);}return map;
}// 判断子串是否包含t
private boolean check(Map<Character, Integer> subStatis, Map<Character, Integer> tStatis) {for (Character c : tStatis.keySet()) {int subCount = subStatis.getOrDefault(c, 0);if (subCount < tStatis.get(c)) return false;}return true;
}

效果:

代码没问题,但是时间复杂度太高,没法满足需求 

滑动窗口

暴力法的缺点是显而易见的:时间复杂度过大,超出了运行时间限制。在哪些方面可以优化呢?

仔细观察可以发现,我们在暴力求解的时候,做了很多无用的比对:对于字符串“ADOBECODEBANC”,当找到一个符合条件的子串“ADOBEC”后,我们会继续仍以“A”作为起点扩展这个子串,得到一个符合条件的“ADOBECO”——它肯定符合条件,也肯定比之前的子串长,这其实是完全不必要的。

代码实现上,我们可以定义两个指针:指向子串“起始点”的左指针,和指向子串“结束点”的右指针。它们一个固定、另一个移动,彼此交替向右移动,就好像开了一个大小可变的窗口、在不停向右滑动一样,所以这就是非常经典的滑动窗口解决问题的应用场景。所以有时候,滑动窗口也可以归类到双指针法。

public String minWindow1(String s, String t) {// 保存自小子串String result = "";// 统计t中字符串数量Map<Character, Integer> tCharStatis = statis(t);int lp = 0;int rp = t.length();while (rp <= s.length()) {String subStr = s.substring(lp, rp);Map<Character, Integer> subCharStatis = statis(subStr);// 子串符合条件,则左指针右移if (check(subCharStatis, tCharStatis)) {if ("".equals(result) || subStr.length() < result.length()) {result = subStr;}lp++;} else {//不符合条件则右指针右移继续寻找rp++;}}return result;
}// 统计字符串中单个字符数量
private Map<Character, Integer> statis(String str) {Map<Character, Integer> map = new HashMap<>();for (int i = 0; i < str.length(); i++) {int count = map.getOrDefault(str.charAt(i), 0);count++;map.put(str.charAt(i), count);}return map;
}// 判断子串是否包含t
private boolean check(Map<Character, Integer> subStatis, Map<Character, Integer> tStatis) {for (Character c : tStatis.keySet()) {int subCount = subStatis.getOrDefault(c, 0);if (subCount < tStatis.get(c)) return false;}return true;
}

效果:

比之前有进步,但是还需进一步优化 

滑动窗口优化

我们判断S是否满足包含T中所有字符的时候,调用的方法check其实又是一个暴力法:遍历T中所有字符频次,一一比对。上面的复杂度分析也可以看出,遍历s只用了线性时间,但每次都要遍历一遍T的频次哈希表,这就耗费了大量时间。

我们已经知道,每次指针的移动,只涉及到一个字符的增减。所以我们其实不需要知道完整的频次HashMap,只要获取改变的这个字符的频次,然后再和T中的频次比较,就可以知道新子串是否符合要求了。

public String minWindow(String s, String t) {// 保存最小子串String result = "";// 统计t中字符串数量Map<Character, Integer> tCharStatis = statis(t);Map<Character, Integer> subCharStatis = new HashMap<>();int lp = 0;int rp = 1;while (rp <= s.length()) {char newChar = s.charAt(rp - 1);// t中包含该字符再统计if (tCharStatis.containsKey(newChar)) {int count = subCharStatis.getOrDefault(newChar, 0);subCharStatis.put(newChar, count + 1);}// 子串符合条件,则左指针右移while (check(subCharStatis, tCharStatis) && lp < rp) {if ("".equals(result) || rp - lp < result.length()) {result = s.substring(lp, rp);}char removedChar = s.charAt(lp);if (tCharStatis.containsKey(removedChar)) {subCharStatis.put(removedChar, subCharStatis.getOrDefault(removedChar, 0) - 1);}lp++;}rp++;}return result;
}// 统计字符串中单个字符数量
private Map<Character, Integer> statis(String str) {Map<Character, Integer> map = new HashMap<>();for (int i = 0; i < str.length(); i++) {int count = map.getOrDefault(str.charAt(i), 0);count++;map.put(str.charAt(i), count);}return map;
}// 判断子串是否包含t
private boolean check(Map<Character, Integer> subStatis, Map<Character, Integer> tStatis) {for (Character c : tStatis.keySet()) {int subCount = subStatis.getOrDefault(c, 0);if (subCount < tStatis.get(c)) return false;}return true;
}

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

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

相关文章

刷题日记:面试经典 150 题 DAY3

刷题日记&#xff1a;面试经典 150 题 DAY3 274. H 指数238. 除自身以外数组的乘积380. O(1) 时间插入、删除和获取随机元素134. 加油站135. 分发糖果 274. H 指数 原题链接 274. H 指数 重要的是都明白H指数到底是是个啥。注意到如果将引用数从大到小排序&#xff0c;则对于…

考研复试指南

1. 记住&#xff0c;复试的本质不是考试&#xff0c;而是一场自我展示。 考研复试并非简单的知识考察&#xff0c;更是一场展示自我能力和潜力的机会。除了学科知识&#xff0c;考官更关注你的综合素质、学术兴趣和未来发展规划。因此&#xff0c;要保持自信&#xff0c;用更全…

机器学习模型总结

多元线性回归&#xff08;linear regression&#xff09; 自变量&#xff1a;连续型数据&#xff0c;因变量&#xff1a;连续型数据 选自&#xff1a;周志华老师《机器学习》P53-55 思想&#xff1a;残差平方和达到最小时的关系式子即为所求&#xff0c;残差平方和&#xff1a…

uniapp 部署h5,pdf预览

1.hubuilderx 打包h5。 2.上传部署包到服务器。 解压部署包&#xff1a;unzip h5.zip 。 3.nginx配置。 user root; worker_processes 1; #worker_cpu_affinity 0001 0010 0100 1000; #error_log logs/error.log; #error_log logs/error.log notice; error_log /var/l…

抖店怎么入驻?具体的入驻流程是什么?新手一看就会!

我是电商珠珠 新的一年开始了&#xff0c;又有不少新手小伙伴入驻了抖店。我做电商已经五年了&#xff0c;做抖店做了三年多&#xff0c;期间带着学员一起做店。所以对于他们所犯的这些操作错误&#xff0c;相信部分新手小伙伴也会犯错&#xff0c;为了让大家少走点弯路&#…

便携式气象站的工作原理

TH-BQX8便携式气象站是一种轻便、易于携带的气象监测设备&#xff0c;它能够快速部署在需要监测的区域&#xff0c;实时监测和记录气象环境数据。与全自动气象监测站相比&#xff0c;便携式气象站更加注重移动性和灵活性&#xff0c;适用于临时性的气象监测任务或特定区域的气象…

【大厂AI课学习笔记NO.58】(11)混淆矩阵

混淆矩阵&#xff08;confusion matrix&#xff09;—— 混淆矩阵&#xff08;Confusion Matrix&#xff09;是人工智能领域&#xff0c;特别是在机器学习和深度学习中&#xff0c;用于衡量分类模型性能的重要工具。它通过统计分类模型的真实分类与预测分类之间的结果&#xf…

12. Nginx进阶-Location

简介 Nginx的三大区块 在Nginx中主要配置包括三个区块&#xff0c;结构如下&#xff1a; http { #协议级别include /etc/nginx/mime.types;default_type application/octet-stream;log_format main $remote_addr - $remote_user [$time_local] "$r…

javascript中对包含关系判断介绍

本文将为您详细讲解 JavaScript 中对包含关系的判断&#xff0c;包括数组、字符串等&#xff0c;并提供相应的代码例子。 1. 数组包含关系判断 在 JavaScript 中&#xff0c;数组包含关系判断通常使用 Array.prototype.includes() 方法。这个方法返回一个布尔值&#xff0c;表示…

辽宁博学优晨教育视频:引领安全可靠的学习新风尚

在数字化时代&#xff0c;随着信息技术的飞速发展&#xff0c;线上教育已成为越来越多人提升自我、拓宽视野的重要选择。辽宁博学优晨教育视频凭借其安全可靠的特质&#xff0c;在众多在线教育平台中脱颖而出&#xff0c;成为广大学子信赖的学习伙伴。 一、辽宁博学优晨教育视频…

Docker创建Reids容器

1.默认拉取Redis最新镜像版本 docker pull redis 2.下载redis配置文件 https://download.redis.io/releases/ 3.下载配置文件后手动更改密码&#xff0c;链接时间等信息 绑定地址&#xff08;bind&#xff09;&#xff1a;默认情况下&#xff0c;Redis 只会监听 localhost…

【MySQL 系列】在 Windows 上安装 MySQL

在 Windows 平台上安装 MySQL 很简单&#xff0c;并不需要太复杂的步骤。按照本文的步骤操练起来就可以了。 文章目录 1、下载 MySQL 安装程序2、安装 MySQL 数据库2.1、选择安装类型2.2、检查所需组件2.3、安装所选产品组件2.4、产品配置2.5、配置高可用性2.6、配置服务器类型…