Leetcode 最小覆盖子串

news2024/11/23 12:30:09

在这里插入图片描述

解题思路:

  1. 哈希表存储字符频率:首先统计字符串 t 中每个字符出现的次数。
  2. 滑动窗口:用两个指针 leftright 来标记当前窗口的左右边界,不断右移 right,直到包含了所有 t 中的字符。然后尝试右移 left,缩小窗口以找到最小覆盖子串。
  3. 更新最小长度:在窗口满足条件时,记录当前窗口的长度并更新最小长度
  4. 时间复杂度:由于左右指针各自只遍历一次字符串,因此时间复杂度为 O(n)。

定义一个变量 count,用来记录当前窗口中还需要匹配多少个字符。初始化时,count 等于 t 的长度。
一旦 count == 0,意味着当前窗口已经包含了 t 中所有字符。此时我们尝试通过右移 left 指针来缩小窗口,并检查最小长度。
HashMap<Character, Integer> 的值是动态变化的。它的值在滑动窗口移动时会随着窗口内字符的增减而调整。具体来说,HashMap 的值记录了当前滑动窗口中对剩余字符需要匹配的次数

Java 代码:

class Solution {
    public String minWindow(String s, String t) {
        //如果m,n可以取整为0,需要对特殊情况返回空串
        if(s.length() == 0 || s == null || t.length() == 0 || t == null) {
            return "";
        }

        //然后,首先统计 t 中字符频率
        HashMap<Character, Integer> TCharfrequency = new HashMap<>();
        for(char c : t.toCharArray()){
            TCharfrequency.put(c, TCharfrequency.getOrDefault(c, 0) + 1);
        }

        //初始化滑动窗口的左右指针,初始化最小覆盖子串的长度
        int left = 0;
        int right = 0;
        int minLeft = 0;
        int minLen = Integer.MAX_VALUE;

        //初始化仍然需要匹配的字符次数 count
        int count = t.length();

        //滑动窗口,HashMap 的value值记录了此时滑动窗口中对应key字符剩余需要匹配的次数,是动态变化的
        while(right < s.length()) {
            //首先处理当前右指针指向的字符
            char rChar = s.charAt(right);
            if(TCharfrequency.containsKey(rChar)) {
                //如果当前右指针指向的字符在map中出现,说明成功匹配了一个字符,需要减少count
                if(TCharfrequency.get(rChar) > 0) {
                //之所以需要判断value大于0,是因为如果value==0,说明当前字符匹配次数达到了,不需要再匹配了,
                    count--;
                }
                //同时我们需要更新hashmap中对应的value
                TCharfrequency.put(rChar, TCharfrequency.get(rChar) - 1);//为什么需要这行代码?
            }

            //处理完当前右指针字符后滑动窗口, 移动右指针
            right++; 

            //每当滑动窗口之后, 立马进行优化缩小窗口尝试

            while(count == 0) { //如果count为0,说明当前窗口已经包含了所有t中的字符
            // 如果使用 if,只能尝试缩小一次,也就是说只能缩小一个字符的范围。
            // 使用 while 可以确保我们在当前窗口仍然满足所有条件的情况下,反复缩小左边界,直到窗口再也不能缩小为止。
                //更新滑动窗口
                if(right - left < minLen) {
                    minLeft = left;
                    minLen = right - left;
                }

                // 在更新了窗口之后,尝试缩小窗口
                char lChar = s.charAt(left);
                if(TCharfrequency.containsKey(lChar)) {
                //存在可能性使得右边界右移的步数小于左边界右移的步数同时保持覆盖子串,
                //所以即使lChar 在 HashMap 中存在,仍然要移动左指针,更新hash表字符频率
                    TCharfrequency.put(lChar, TCharfrequency.get(lChar) + 1); 
                //如果hash表中包含左指针的字符,那么我们仍然移动右移左指针
                    if(TCharfrequency.get(lChar) > 0) {
                        count++;//更新待匹配次数计数器
                    }
                }
                left++; 
            }
        }
        //substring 左闭右开
        return minLen == Integer.MAX_VALUE ? "" : s.substring(minLeft, minLeft + minLen);
    }
}

为什么即使 lChar 在 HashMap 中存在,仍然要移动左指针?

是因为有可能右边界右移的步数小于左边界右移的步数,并且仍然保持覆盖子串,因此即使 lChar 在 HashMap 中存在,我们仍然需要继续移动左指针,尝试缩小窗口。

count 的更新和 Hashmap 频率更新的先后顺序,在处理右边界字符时,是先更新count 然后更新 Hashmap 频率,但是在处理左边界字符时,是先更新 Hashmap 频率,然后更新count,请你分析这种先后顺序的必要性

你观察得非常精准,的确在处理右边界和左边界时,countHashMap 频率的更新顺序不同。这种先后顺序是有必要的,因为它保证了算法在不同情况下的正确性和有效性。下面,我会详细分析为什么在右边界和左边界的处理过程中,countHashMap 的更新顺序要有所区别。

右边界处理顺序:先更新 count,再更新 HashMap 频率

if (map.containsKey(rChar)) {
    if (map.get(rChar) > 0) {
        count--;  // 如果当前字符在t中且频率大于0,说明它是需要匹配的,count减1
    }
    map.put(rChar, map.get(rChar) - 1);  // 更新字符频率,表示窗口已经包含了该字符
}

原因分析

  1. count 的含义
    count 表示窗口中还需要匹配的字符数量。也就是说,当 count > 0 时,窗口还未完全覆盖字符串 t 中的所有字符。当 count == 0 时,窗口恰好包含了 t 中的所有字符。

  2. 为什么先更新 count
    在右边界扩展时,首先判断当前窗口能否满足字符匹配的需求。如果当前字符 rChar 是目标字符串 t 中的字符,并且它在 HashMap 中的频率大于 0,说明这是一个仍然需要匹配的字符,因此我们首先更新 count,将它减 1,表示这个字符的匹配需求被满足了。

    这样做的原因是:

    • 防止遗漏有效字符:在 count-- 之前,我们需要确保这个字符是否真正是需要的。因为 map.get(rChar) > 0 代表这个字符在窗口中还不够,因此我们首先减去 count 表示该字符的匹配需求已被满足。
    • 保证窗口的完整性:如果先更新了 HashMap,可能会导致误判。假设先更新了 HashMap 的频率值(即减去 1),然后再去检查 count,此时可能会因为 HashMap 已经发生变化,而错过本应该减少的 count
  3. 更新 HashMap 频率
    在减少 count 之后,再更新 HashMap,将 rChar 的频率减 1,表示该字符已经被包含在窗口中。这一步保证了后续右指针继续移动时,窗口的状态是准确的。

左边界处理顺序:先更新 HashMap 频率,再更新 count

if (map.containsKey(lChar)) {
    map.put(lChar, map.get(lChar) + 1);  // 更新频率,表示移出窗口的字符需要重新匹配
    if (map.get(lChar) > 0) {
        count++;  // 如果频率变为正数,说明需要再次匹配该字符,count增加
    }
}

原因分析

  1. count 的含义保持不变
    count 在左边界移动时,仍然表示窗口中还需要匹配的字符数量。当窗口左边界字符被移出后,如果该字符属于 t 中的字符,我们需要判断移出该字符后,窗口中是否还包含足够数量的该字符。

  2. 为什么先更新 HashMap
    在左边界移动时,首先需要更新 HashMap 的频率,表示我们即将移除左边界的字符 lChar。如果 lChart 中的字符,我们将它的频率加 1,表示窗口中少了一个这样的字符。

    这样做的原因是:

    • 准确判断窗口状态:只有先更新了 HashMap,我们才能准确判断窗口中是否缺少了某个字符。如果不先更新 HashMap,就无法知道这个字符移出后窗口是否仍然有效(即是否还包含足够的 t 中字符)。
    • 避免过早增加 count:如果我们在更新 HashMap 之前就更新 count,可能会错误地增加 count,而事实上,窗口中的字符数量可能仍然是足够的。
  3. 更新 count
    HashMap 频率更新之后,我们检查频率是否变成了正数。如果是正数,说明窗口中的该字符数量已经不足,因此需要将 count 增加,表示该字符需要重新匹配。

顺序对比总结:

  1. 处理右边界时

    • 先更新 count 是为了确保在当前字符进入窗口时,能够及时检查它是否满足匹配需求。只有在 count 减少之后,才能表明该字符被正确匹配。
    • 后更新 HashMap 是为了保证窗口的状态准确地反映已经包含的字符频率。
  2. 处理左边界时

    • 先更新 HashMap 是为了表示当前字符即将从窗口中移除。只有更新了字符的频率之后,才能判断窗口是否还满足条件。
    • 后更新 count 是因为我们需要在频率变化之后才能判断该字符是否已经不足。如果移出字符后,窗口中还需要再次匹配该字符,那么 count 才会增加。

必要性总结:

  • 右边界的顺序确保了窗口扩展时能够正确减少 count,保证每次右边界移动后,我们都能够实时判断窗口是否满足匹配条件。
  • 左边界的顺序确保了在缩小窗口时,我们能够准确更新窗口的状态,防止误判窗口是否仍然满足所有条件。

这个先后顺序的设计,确保了算法在扩展和收缩窗口时,都能够正确地处理字符匹配问题,从而确保我们能找到最小的覆盖子串。

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

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

相关文章

python爬虫/引用requests/基本使用

1.安装requests 进入控制台使用该命令安装requests pip3 install requests 2.对网站使用get请求 这里用对网站进行get请求&#xff0c;然后打印。 import requests //引用requestsresponse requests.get(urlhttps://www.bilibili.com/)print(response.text) 3.对网站使用…

2024全国研究生数学建模竞赛(数学建模研赛)ABCDEF题深度建模+全解全析+完整文章

全国研究生数学建模竞赛&#xff08;数学建模研赛&#xff09;于9月21日8时正式开赛&#xff0c;赛程4天半&#xff0c;咱这边会在开赛后第一时间给出对今年的6道赛题的评价、分析和解答。包括ABCDEF题深度建模全解全析完整文章&#xff0c;详情可以点击底部的卡片来获取哦。 …

座椅空置状态检测系统源码分享

座椅空置状态检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

大模型之基准测试集(Benchmark)-给通义千问2.0做测评的10个权威测基准测评集

引言 在去年(2023)云栖大会上&#xff0c;阿里云正式发布千亿级参数大模型通义千问2.0。据现场介绍&#xff0c;在10个权威测评中&#xff0c;通义千问2.0综合性能超过GPT-3.5&#xff0c;正在加速追赶GPT-4。以下是通义千问在MMLU、C-Eval、GSM8K、HumanEval、MATH等10个主流…

基于Springboot共享充电宝管理系统JAVA|VUE|SSM计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

openEuler普通用户su root时Permission denied

openEuler普通用户su root时Permission denied 背景&#xff1a; openEuler默认普通用户是不能通过su切换到root用户的 如果想通过su切换到root&#xff0c;有以下两个解决办法 1、修改/etc/pam.d/su 文件 [rootlocalhost ~]# vim /etc/pam.d/su #修改21行&#xff0c;将“…

视频怎么制作成二维码?视频轻松生成二维码的3步操作

现在很多人为了能够更快捷的实现视频内容的分享&#xff0c;会通过将视频生成二维码的方式&#xff0c;让其他人可以通过扫描二维码来查看视频内容。这种方式不需要用户存储视频&#xff0c;扫码就能够在设备上查看视频&#xff0c;有利于提升查看视频的便捷性&#xff0c;可以…

图片压缩工具免费怎么找?归纳了这几个压缩工具

有哪些图片压缩工具免费&#xff1f;在数字化时代&#xff0c;图像已成为我们生活中不可或缺的一部分。无论是网站设计、社交媒体分享还是文件传输&#xff0c;高质量的图片都扮演着重要的角色。但高质量往往意味着大文件体积&#xff0c;这可能会导致加载速度变慢或存储空间不…

打造以太坊数据监控利器:InfluxDB与Grafana构建Geth可视化分析平台

前言 以太坊客户端收集大量数据&#xff0c;这些数据可以按时间顺序数据库的形式读取。为了简化监控&#xff0c;这些数据可以输入到数据可视化软件中。在此页面上&#xff0c;将配置 Geth 客户端以将数据推送到 InfluxDB 数据库&#xff0c;并使用 Grafana 来可视化数据。 一…

Android13中Android.mk和Android.bp预编译多种架构文件

需求&#xff1a; 1&#xff0c; 当前有多个架构的config文件&#xff0c;但是需要不同架构使用不同config文件 2&#xff0c; 必须将config文件拷贝到out/host目录下 常规思路 在Android.bp中&#xff0c; 一般在编译多架构文件时&#xff0c;都会使用arch属性&#xff…

Tauri 应用 input 输入自动大写问题定位解决

使用 Tauri React 开发 MinApi(http api接口测试工具) 时&#xff0c;在 Mac 系统中遇到一个很奇怪的问题&#xff1a;在 input 输入框中输入内容时&#xff0c;如果输入的是全小写英文字母&#xff0c;会自动将首字母转换为大写&#xff0c;效果如下图所示。 问题定位 经过排…

WebRTC关键技术及应用场景:EasyCVR视频汇聚平台高效低延迟视频监控解决方案

众所周知&#xff0c;WebRTC是一项开源的实时通信技术&#xff0c;它通过集成音频、视频和数据传输到Web浏览器中&#xff0c;使得实时通信变得简单且无需任何插件或第三方软件。WebRTC不仅是一个API&#xff0c;也是一系列关键技术和协议的集合&#xff0c;它的出现改变了传统…

代码随想录算法训练营Day14 | 226.翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

目录 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度 226.翻转二叉树 题目 226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例1&#…

Android下反调试与反反调试

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 反调试检测 反调试检测的几种方式。 1. TrackerId 首先&#xff0c;通过 IDA Pro 的调试器附加到当前 app 进程 关于IDA Pro调试android app的详细教程可以…

必应广告投放推广收费标准和流程

在当今竞争激烈的商业环境中&#xff0c;如何精准高效地推广产品与服务&#xff0c;成为企业面临的重大挑战。微软必应Bing广告平台&#xff0c;凭借其强大的技术实力和精准的数据分析能力&#xff0c;已成为众多企业广告推广的首选。云衔科技作为业界领先的数字化营销服务商&a…

【机器学习-无监督学习】聚类

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

安卓系统升级后,关于Fiddler工具不能抓取https接口问题

问题原因&#xff1f; 目前安卓手机可以抓取的https接口都在安卓7.0版本以下&#xff0c;有时候抓取Android7.0版本或以上的接口抓取不到 因为Android7.0之后常规手段不能抓Https的包&#xff0c;应用会默认不信任用户安装的证书(手机里自己安装的证书)&#xff0c;只信任系统…

最新版FaceFusion3.0.0,最强AI换脸,表情修改,视频换脸,年龄修改,多人换脸,面部遮挡换脸,参数调优

主要修改&#xff1a;表情修改,视频换脸,年龄修改,多人换脸,面部遮挡换脸,参数调优 变更日志 改造一切皆工作的建筑介绍pixel boost换脸者为面部检测器添加多角度处理引入年龄修正处理器推出 Live Portrait 表情恢复处理器推出由 Live Portrait 提供支持的脸部编辑处理器用res…

视频制作软件哪个好?前十名推荐!

在视频制作领域&#xff0c;选择合适的软件是提升创作效率和作品质量的关键。本文将根据软件的适用人群&#xff1a;新手入门和专业领域&#xff0c;以及推荐的书籍&#xff0c;为您详细介绍视频制作软件的前十名。 新手入门级别&#xff1a; 1.影忆 功能特点&#xff1a;新手入…

浙大数据结构:05-树9 Huffman Codes

这道题难度挺大&#xff0c;写起来较为费劲&#xff0c;这里我依然使用了STL库&#xff0c;使得代码量大幅减少不过百行&#xff0c;便于大家理解。 机翻&#xff1a; 1、条件准备 数组存储字符对应频率&#xff0c;n,student存储输入多少字符&#xff0c;有多少学生测试。 …