最小覆盖子串(Java详解)

目录

一、题目描述

二、题解


一、题目描述

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。

如果 s 中存在多个符合条件的子字符串,返回任意一个。

注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

示例:

输入:s = "ADOBECODEBANC", t = "ABC"

输出:"BANC"

输入:s = "a", t = "a"

输出:"a"

二、题解

思路分析:题目要求我们找到 s 中包含 t 的所有字符的最短子字符串,即找到的子串中必须含有 t 中所有字符,可以有其他字符,返回其中最短的子串。

首先,我们很容易想到可以通过暴力枚举的方式来找到最小覆盖子串

每次记录子串的长度,并更新记录的最短子串,当i 遍历完 s 时,找到最小子串,由于当 i 找到 t 中字符时,要使用 j 向后遍历,找到子串,因此,暴力枚举的时间复杂度为O(n^{2}) ,

当找到子串时,i 向后移动,直到再次找到 t 中字符,此时再向后寻找子串,

当再次向后寻找子串时,j可能向后移动,也可能保持不动

 

因此,我们可以不用每次找到 t 中字符时,都从当前下一位置向后寻找,只需从 j 上次记录的位置开始向后寻找

此时,我们可以考虑使用滑动窗口来解决本题,即使用left 和 right 两个指针维护一个窗口,窗口中不包含 t 中所有字符时,right向右滑动,添加新的字符,当窗口中包含 t 中所有字符时,判断并更新最小子串,再将left 向右滑动,移除左边元素

解题步骤:

1. 使用哈希表记录 t 中字符的种类和个数

2. 定义left 和 right 指针遍历s,并维护窗口

3. 当窗口中不含有 t 中所有字符时,向右移动 right ,添加新的字符;

当窗口中含有 t 中所有字符时,判断并更新最小子串,再向右移动 left ,直到移除t中的一个字符

再分析清楚过程后,我们可以尝试编写代码来解决本题

首先我们使用哈希表统计字符的种类和长度:

//1. 
//特殊情况:若s的长度小于t,直接返回空字符串
if(s.length() < t.length()){
    return "";
}
//使用哈希表统计t中字符的种类和长度
//由于题目中给出s和t由英文字母组成,因此我们可以使用数组模拟哈希表
int[] hash1 = new int[128];
//记录t中字符的种类和数量
for (int i = 0; i < t.length(); i++) {
    hash1[t.charAt(i)]++;
}

 接下来我们遍历s并维护窗口:

//2. 
int begin = -1, minLen = -1;//记录最小子串的起始位置和长度
int[] hash2 = new int[128];//记录s中字符的种类和个数

//使用left和right维护窗口
for (int left = 0,right = 0; right < s.length(); right++) {
    //右边字符进窗口
    char in = s.charAt(right);
    hash2[in]++;

    //判断是否需要出窗口
    
        //更新结果
        //左边元素出窗口
        
        

    }
}

如何判断是否需要出窗口?

当子串包含t中所有字符时,需要出窗口。由于我们使用数组来模拟哈希表,我们可以通过遍历的方式来判断hash2中是否包含hash1中所有字符,然而,这种方式效率较低,那么我们该如何改进呢?

我们可以使用变量 count 来统计子串中有效字符(当前字符ch为 t 中字符,且此时窗口中ch的数量 小于等于 t 中 ch个数的个数。此时,在判断出窗口时,仅需判断count 是否 大于等于 t 的长度,若大于,则出窗口

使用count来标记子串中所含有的 t 中字符个数

 注意,当前字符为 t 中字符,也可能不为有效字符,例如:

此时,虽然A为t中字符,但窗口中A的个数大于 t 中A的个数,此时的字符A不能判断为有效字符

//2.
int begin = -1, minLen = -1;//记录最小子串的起始位置和长度
int[] hash2 = new int[128];//记录s中字符的种类和个数
//使用left和right维护窗口
int count = 0;//记录窗口中有效字符的个数
for (int left = 0,right = 0; right < s.length(); right++) {
    //右边字符进窗口
    char in = s.charAt(right);
    hash2[in]++;
    //判断是否是有效字符
    //先将字符放入哈希表后,再判断
    if(hash2[in] <= hash1[in]){
        count++;
    }

    //判断是否需要出窗口
    //出窗口的条件:当有效字符的个数大于等于t的长度
    while (count >= t.length()){
        //更新结果
        if(right - left + 1 < minLen || begin == -1){
            begin = left;
            minLen = right - left + 1;
        }
        //将左边元素出窗口
        char out = s.charAt(left);
        //判断是否是有效字符出窗口
        //先判断当前字符是否是有效字符后,再出窗口
        if(hash2[out]-- <= hash1[out]){
            count--;
        }
        //左边元素出窗口
        left++;
    }
}

最后,再返回最小覆盖子串即可

完整代码:

class Solution {
    public String minWindow(String s, String t) {
       //1.
        //特殊情况:若s的长度小于t,直接返回空字符串
        if(s.length() < t.length()){
            return "";
        }
        //使用哈希表统计t中字符的种类和长度
        //由于题目中给出s和t有英文字母组成,因此我们可以使用数组模拟哈希表
        int[] hash1 = new int[128];
        //记录t中字符的种类和数量
        for (int i = 0; i < t.length(); i++) {
            hash1[t.charAt(i)]++;
        }
        
        //2.
        int begin = -1, minLen = -1;//记录最小子串的起始位置和长度
        int[] hash2 = new int[128];//记录s中字符的种类和个数
        //使用left和right维护窗口
        int count = 0;//记录窗口中有效字符的个数
        for (int left = 0,right = 0; right < s.length(); right++) {
            //右边字符进窗口
            char in = s.charAt(right);
            //判断是否是有效字符
            //先将字符放入哈希表后,再判断
            if(++hash2[in] <= hash1[in]){
                count++;
            }

            //判断是否需要出窗口
            //出窗口的条件:当有效字符的个数大于等于t的长度
            while (count >= t.length()){
                //更新结果
                if(right - left + 1 < minLen || begin == -1){
                    begin = left;
                    minLen = right - left + 1;
                }
                //将左边元素出窗口
                char out = s.charAt(left);
                //判断是否是有效字符出窗口
                //先判断当前字符是否是有效字符后,再出窗口
                if(hash2[out]-- <= hash1[out]){
                    count--;
                }
                //左边元素出窗口
                left++;
            }
        }
        //遍历完成 返回最小子串
        return begin == -1 ? "": s.substring(begin,begin+minLen);
    }
}

题目来自:

LCR 017. 最小覆盖子串 - 力扣(LeetCode)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【IO】IO模型与零拷贝

前言&#xff1a; 正在运行的程序其实就是系统中的一个进程&#xff0c;操作系统会为每一个进程分配内存空间&#xff0c;而内存空间分为两部分&#xff0c;一部分是用户空间&#xff0c;这是用户进程访问的内存区域&#xff1b;另一部分是内核空间&#xff0c;是操作系统内核访…

详解Keras3.0 Layer API: LSTM layer

LSTM layer 用于实现长短时记忆网络&#xff0c;它的主要作用是对序列数据进行建模和预测。 遗忘门&#xff08;Forget Gate&#xff09;&#xff1a;根据当前输入和上一个时间步的隐藏状态&#xff0c;计算遗忘门的值。遗忘门的作用是控制哪些信息应该被遗忘&#xff0c;哪些…

vue2、vue3状态管理之vuex、pinia

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、状态管理之vuex1.1 State调用&#xff1a;1.2 Mutation在vuex中定义&#xff1a;在组件中使用&#xff1a; 1.3 Action在vuex中定义&#xff1a;将上面的减…

Vue 自定义ip地址输入组件

实现效果&#xff1a; 组件代码 <template><div class"ip-input flex flex-space-between flex-center-cz"><input type"text" v-model"value1" maxlength"3" ref"ip1" :placeholder"placeholder"…

VMware之FTP的简介以及搭建使用计算机端口的介绍

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、FTP介绍 1、什么是FTP&#xff1a; 2、FTP适用于以下情况和应用场景&#xff1a; 3、winServer2012搭…

Verilog置换处理脚本

文章目录 一、介绍二、脚本 一、介绍 在Verilog中的置换处理&#xff0c;为将一个数据的数据位按照某种规则进行重新排列。 以DES算法的初始置换为例 初始置换将64比特的明文&#xff0c;按照初始置换表进行置换&#xff0c;得到一个乱序的64bit明文组。 初始置换表如下&…

加速计算,为何会成为 AI 时代的计算力“新宠”

随着科技的发展&#xff0c;处理大量数据和进行复杂计算的需求越来越高&#xff0c;人工智能、大数据和物联网等领域更是如此&#xff0c;传统的计算方式已经无法满足这些需求。因此&#xff0c;加速计算作为一种现代计算方式&#xff0c;成了必要的手段。加速计算具有前所未有…

为什么设计制造行业需要数据加密?

设计制造行业是一个涉及多种技术、工艺、材料和产品的广泛领域&#xff0c;它对经济和社会的发展有着重要的影响。然而&#xff0c;随着数字化、智能化和网络化的发展&#xff0c;设计制造行业也面临着越来越多的数据安全风险&#xff0c;如数据泄露、数据篡改、数据窃取等。这…

Qt Creator可视化交互界面exe快速入门4

上一期介绍了信号与槽&#xff0c;本期介绍加法计算器 我们来新建一个项目 然后拖动设置按钮 还需要个输出框 这里拖动Line Edit 我这里只是简单演示一下&#xff0c;做个低配版计算器&#xff0c;再加个加号和一个等于号就结束了。 然后回到代码编辑部分&#xff0c;我们需要…

代码随想录27期|Python|Day29|回溯算法|491.递增子序列|46.全排列|47.全排列 II

491. 非递减子序列 本题不是单纯的去重题目&#xff0c;而是需要保持数字在原数组的顺序。 比如&#xff1a;[4,5,6,7]和[4,6,5,7]相比&#xff0c;后者就不能选择[5,6,7]这个排列&#xff0c;因为违反了设置的顺序。所以去重的方法就只有哈希表。 需要在每一层设置一个哈希表…

注册谷歌企业开发者账号所需的邓白氏码是什么?如何获取?以及相关费用?

随着谷歌政策的收紧&#xff0c;谷歌对个人开发者账号发布应用的要求越来越高&#xff0c;需要20人连续测试14天&#xff0c;才能提审&#xff0c;因此很多开发者选择使用企业账号来进行上架。 而众所周知&#xff0c;注册谷歌企业开发者账号需要邓白氏码。什么是邓白氏码&…

制作系统U盘启动surface教程

最近本人是崩溃的&#xff1a;我surface pro9系统之前被我更新成win11 dev的开发预览版&#xff0c;不好用&#xff0c;有很多bug&#xff0c;升级后才发现已经是预览版成员资格&#xff0c;回退不了&#xff0c;重置初始化依然是dev预览版和取消预览计划是灰色的&#xff0c;退…

Volume Control 2

为游戏添加音乐和音效总是需要一些编码来设置一个系统来控制、显示和保存应用程序的音量设置。 音量控制的设计是为了立即为您设置这些内容,让您有更多时间专注于最重要的事情——制作出色的游戏! 在版本2中,我们对系统进行了重新设计,使其更加模块化、灵活,甚至更易于使用…

MySQL——索引

目录 一.没有索引&#xff0c;可能会有什么问题 二.MySQL与存储 1.先来研究一下磁盘 2.MySQL与磁盘交互基本单位 3.建立共识与总结 三.索引的理解 三.索引操作 1.创建主键索引 2.唯一索引的创建 3.普通索引的创建 4.全文索引的创建 四.查询索引 五.删除索引 一…

MySQL进阶SQL语句

1、select 显示表格种一个或数个字段的所有数据记录 语法&#xff1a;select "字段" from "表名"; 2、distinct 不显示重复的数据记录 语法&#xff1a;select distinct "字段" from "表名"; 3、where 有条件查询 语法&#x…

模式识别与机器学习-SVM(核方法)

SVM&#xff08;核方法&#xff09; 核方法核技巧在SVM中的应用 谨以此博客作为复习期间的记录 核方法 对解线性分类问题&#xff0c;线性分类支持向量机是一种非常有效的方法&#xff0e;但是&#xff0c;有时分类问题是非线性的&#xff0c;这时可以使用非线性支持向量机&a…

线程池原理及使用

线程池继承关系 1.为什么使用线程池&#xff1f; 1.反复创建线程开销大; 2.过多线程会占用太多内存(执行任务易出现“内存溢出”); 3.加快程序响应速度; 4.合理利用CPU和内存; 5.统一管理线程; 2.创建和停止线程池 2.1.线程池参数解释 1.keppAliveTime 如果线程池当中的线程数…

使用Python构建令人瞩目的高频交易算法

大家好&#xff0c;在金融领域&#xff0c;高频交易&#xff08;HFT&#xff09;因其能够以极高的速度执行大量订单的能力而备受关注。高频交易算法旨在识别并利用不同市场间的微小价格差异&#xff0c;因此交易者需要实现低延迟系统来进行套利策略&#xff0c;本文将探索使用P…

我的NPI项目之Android系统升级 - 同平台多产品的OTA

因为公司业务中涉及的面比较广泛&#xff0c;虽然都是提供移动终端PDA&#xff0c;但是使用的场景很多时候是不同的。例如&#xff0c;有提供给大型物流仓储的设备&#xff0c;对这样的设备必需具备扫码功能&#xff0c;键盘&#xff08;戴手套操作&#xff09;&#xff0c;耐用…

大数据求职心得

........................................................................................................................................................... 大数据求职心得 ...................................................................................…