滑动窗口(8)_最小覆盖字串

news2024/11/15 14:01:06

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

滑动窗口(8)_最小覆盖字串

收录于专栏【经典算法练习
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 题目链接:

2. 题目描述 :

3. 解法 :

    解法一(暴力枚举) + 哈希表 :

    算法思路 :

    代码展示 :

    结果分析 :

    对暴力算法的反思与优化 :

    解法二(滑动窗口) :

    算法思路 :

    算法流程 :

    代码展示 :

    结果分析 :

4. 总结


1. 题目链接:

OJ链接:最小覆盖字串

2. 题目描述 :

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

3. 解法 :

    解法一(暴力枚举) + 哈希表 :

    算法思路 :

1. 用两层循环遍历整个数组

2. 用两个哈希表分别存储s和t的字符

3 当s的哈希表全部包含t哈希表中的数据,返回有效长度

4. 内层循环回退,外层循环++,进入下一层循环

5. 返回对有效长度取min的最后一个值即为我们所需的最小覆盖字串

 

    代码展示 :

class Solution {

    bool check(int a[], int b[])
    {
        for(int i = 0; i < 128; i++)
            if(b[i] > a[i]) return false;
        return true;
    }
public:
    string minWindow(string s, string t) {
        int minlen = INT_MAX, left = 0;
        int hash1[128] = {0};
        int flag = 1;
        for(auto ch : t)
            hash1[ch]++;

        for(int i = 0; i < s.size(); i++)
        {
            int hash2[128] = {0};
            for(int j = i; j < s.size(); j++)
            {
                hash2[s[j]]++;
                if(check(hash2, hash1)) 
                {
                    flag = 0;
                    if(j - i + 1 < minlen)
                    {
                        minlen = j - i + 1;
                        left = i;
                    } 
                    break;
                }
            }
        }
        if(flag) return "";
        else return s.substr(left, minlen);
    }
};

 

    结果分析 :

不出所料,由于题目中字符串s和t的长度达到了10^5,我们的暴力枚举时间复杂度为O(N^2),整体的数据级别达到了10^10,计算机无法在1s之内完成,会给出超出时间限制的错误

    对暴力算法的反思与优化 :

1. check函数

我们其实可以发现我们的check函数在这道题中对我们的时间复杂度有很大的影响:
我们每比较一次就需要在check函数中遍历128次,所以我们的整体时间复杂度为128*10^10,这个就可以采用变量替换我们的check函数

2. 两层循环

我们能不能将两层循环优化成一层呢?不需要再让j移动一段距离后又重新回到i的位置,采用滑动窗口的方法,让[i,j]这个区间是合法区间

 check函数优化

我们这里舍去了check函数,改用len和count统计:
len统计hash1中有效字符的数量

count统计hash2中有效字符的数量

注意:这里两个变量统计的是字符数量而不是个数是因为t字符可能会重复,比如:AAC 

class Solution {

public:
    string minWindow(string s, string t) {
        int minlen = INT_MAX, left = 0, len = 0;
        int hash1[128] = {0};
        int flag = 1;
        for(auto ch : t)
        {
            if(hash1[ch] == 0) len++;
            hash1[ch]++;
        }

        for(int i = 0; i < s.size(); i++)
        {
            int hash2[128] = {0}, count = 0;
            for(int j = i; j < s.size(); j++)
            {
                hash2[s[j]]++;
                if(hash2[s[j]] == hash1[s[j]]) count++;
                if(count == len) 
                {
                    flag = 0;
                    if(j - i + 1 < minlen)
                    {
                        minlen = j - i + 1;
                        left = i;
                    } 
                    break;
                }
            }
        }
        if(flag) return "";
        else return s.substr(left, minlen);
    }
};

 

比之前多通过了一个例子,看来我们得优化还是有效果的

不过最后还是没能通过这道题主要是因为我们的暴力循环根本时间复杂度为O(N^2),这个改不掉的,所以接下来我们要对暴力算法的两层循环进行优化,将其优化成一层循环就能解决. 

    解法二(滑动窗口) :

    算法思路 :

研究对象是连续的区间,因此可以尝试使用滑动窗口的思想来解决

如何判断当前窗口内的所有字符是符合要求的呢?

我们可以使用两个哈希表, 其中一个将目标串的信息统计起来,另一个哈希表的动态的维护窗口内字符串的信息.

当动态哈希表中包含目标串中所有的字符,并且对应的个数都不小于目标串的哈希表中各个字符的个数,那么当前窗口就是一种可行的方案

    算法流程 :

1. 定义两个全局的哈希表: 1号哈希表hash1用来记录字串的信息,2号哈希表hash2用来记录目标串t的信息;

2. 实现一个接口函数,判断当前窗口是否满足要求:

        遍历两个哈希表中对应的元素:

                如果t中某个字符的数量大于窗口字符的数量,也就是2号哈希表某个位置大于1号哈希表.说明不匹配,返回false

                如果全匹配返回true

    代码展示 :

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = {0}, hash2[128] = {0};
        int len = 0, minlen = INT_MAX, begin = -1;
        for(auto ch : t)
            if(hash1[ch]++ == 0) len++;

        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            if(++hash2[s[right]] == hash1[s[right]]) count++;
            while(count == len)
            {
                if(right - left + 1 < minlen)
                {
                    minlen = right - left + 1;
                    begin = left;
                }
                if(hash2[s[left]]-- == hash1[s[left]]) count--;
                left++;
            }
        }
        return begin == -1 ? "" : s.substr(begin, minlen);
    }
};

 

    结果分析 :

这段代码的时间复杂度分析如下:

初始化字符计数:遍历字符串 t,计算每个字符的频率,时间复杂度为
O(m),其中m 是字符串 t 的长度。

双指针遍历字符串 s:

外层 for 循环遍历字符串 s,时间复杂度为O(n)其中n 是字符串 s 的长度。
内层 while 循环在最坏情况下会遍历字符串 s,但由于 left 和 right 只会各自移动一次,因此总体上 while 循环也可以认为是O(n)

综合以上分析,整体的时间复杂度为:
O(m + n)

这里m 是字符串 t 的长度,n 是字符串 s 的长度。这个复杂度是非常高效的,适合处理较大的输入。

4. 总结

这一道题是滑动窗口完结篇,滑动窗口完结撒花!!!

滑动窗口这个一个专栏共8道题,希望能帮助大家彻底掌握滑动窗口

下个算法专栏就是二分算法

对算法感兴趣的宝子们赶紧点赞收藏起来吧!!!我们明天见!!!

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

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

相关文章

6.C++程序中的基本数据类型

数据类型是指在C中用于声明不同类型变量或函数的一个系统或抽象或者是一个分类&#xff0c;它决定了变量存储占用的内存空间以及解析存储的位模式。其实数据类型可以理解为固定内存大小的别名&#xff0c;是创建变量的模具&#xff0c;具体使用哪种模具&#xff08;包括自定义&…

基于深度学习的文本情感原因提取研究综述——论文阅读

前言 既然要学习情感分析&#xff0c;那么肯定还要了解情感原因对抽取的发展历程&#xff0c;所以我又搜了一篇研究综述&#xff0c;虽然是2023年发表的&#xff0c;但是里面提及到的历程仅停留到2022年。这篇综述发布在TASLP期刊&#xff0c;是音频、声学、语言信号处理的顶级…

进程间的通信-信号量

信号量 1.资源竞争 资源竞争 : 当多个进程同时访问共享资源时&#xff0c;会产生资源竞争&#xff0c;最终最导致数据混乱临界资源 : 不允许同时有多个进程访问的资源&#xff0c;包括硬件资源(CPU、内存、存储器以及其他外围设备)与软件资源(共享代码段、共享数据结构)临界区…

有关JS下隐藏的敏感信息

免责声明&#xff1a;本文仅做分享&#xff01; 目录 JavaScript 介绍 核心组成 工具 FindSomething ** 浏览器检查 ** LinkFinder URLfinder ** SuperSearchPlus ** ffuf ParasCollector waymore Packer Fuzzer JS逆向 应用&#xff1a; 小结&#xff1a; Ja…

基于Python+SQLite的课程管理系统

系统需求简介 1.1需求分析 实现一个具体的课程管理系统。按照软件工程思路设计简化的专业课数据库&#xff0c;尽量模拟现有专业课程一个学期的选课排课原型实际情况。&#xff08;注&#xff1a;本系统由本人单独设计、开发完成&#xff09; 1.2 数据结构需求分析 课程管理…

NAND闪存:迎来新的发展机遇

2024年&#xff0c;存储市场正在经历着动态的变化&#xff0c;其中包括合同价格的上涨、制造商营收的增长以及多项技术上的突破。在这个背景下&#xff0c;主要的存储公司正在为新的挑战做准备&#xff0c;尤其是在NAND闪存领域即将面临转型的情况下。 扩展阅读&#xff1a;20…

PMData:人工智能之运动记录数据集

简介 在这个数据集中&#xff0c;我们展示了 PMData 数据集&#xff0c;旨在将传统的生活记录与体育活动记录相结合。这样的数据集可以开发几个有趣的分析应用程序&#xff0c;例如&#xff0c;可以使用额外的运动数据来预测和分析日常发展&#xff0c;如人的体重和睡眠模式&a…

数据飞轮:打造业务增长的持续循环

在当今数据驱动的世界中&#xff0c;企业必须利用数据的力量才能保持竞争力。然而&#xff0c;仅仅收集和分析数据是不够的&#xff1b;企业必须能够从他们的数据中创造一个持续增长的循环&#xff0c;才能保持成功。其中一种方法就是创建数据飞轮。接下来让我们来探讨一下什么…

centos7如何连接网络 centos7wifi连接

这段时间重新学习 Linux 知识&#xff0c;用的是笔记本&#xff0c;连接的是无良房东家的 WiFi&#xff0c;IP地址经常变动。每次都要修改 Xshell 的配置才能连上虚拟机。效率很低。 为此&#xff0c;必须要解决这个 IP 地址经常变动的事情&#xff01;这里讲解的版本是&#…

Java集成gdal 处理解析tiff和shp数据

1. 配置 gdal 1.1. 官网下载 这个是因为你电脑是 win64 位才选择哦&#xff5e; 下载这个&#xff0c;然后解压 1.2. 复制这个压缩包下的 ddl 文件 可以按照类型复制&#xff0c;然后复制到你的 java jDK 文件夹下 1.3. 找到你的 java jdk 文件夹 不知道 java 的文件夹位置…

数字签名和CA数字证书的核心原理

看了蛋老师的视频就很容易理解了&#xff0c;首先对服务器的公钥和信息进行哈希运算得到一个短字符串&#xff0c;然后用CA机构中的私钥对这一短字符串进行加密就得到了一个数字签名&#xff0c;然后就这个数字签名放到数字证书中&#xff0c;同时服务器的公钥也放在数字证书中…

NFT Insider #148:The Sandbox 推出 SHIBUYA Y3K 时尚系列,Azuki 进军动漫 NFT 领域

市场数据 加密艺术及收藏品新闻 Infinex 新推 NFT 系列首四日销售额破4000万美元 尽管顶级 NFT 系列表现不佳&#xff0c;Infinex 的最新 NFT 系列在首四日内销售额已超过 4000 万美元。Infinex 是一个非托管平台&#xff0c;提供轻松访问链上协议和 dApp。 Infinex Core 的…

Day69补 前后端分离思想

ajax前后端分离 前后端分离处理&#xff1a;前端------&#xff08;数据&#xff09;-----服务端----&#xff08;数据&#xff09;-----前端-----动态改变页面的内容 1.json 1、JSON&#xff1a;由于JSON易读以及纯文本格式的特性&#xff0c;可以非常容易地与其他程序进行沟通…

引领长期投资新篇章:价值增长与财务安全的双重保障

随着全球金融市场的不断演变&#xff0c;长期投资策略因其稳健性和对价值增长的显著推动作用而日益受到投资者的重视。在这一背景下&#xff0c;Zeal Digital Shares&#xff08;ZDS&#xff09;项目以其创新的数字股票产品&#xff0c;为全球投资者提供了一个全新的长期投资平…

IPv6(四)

文章目录 Path MTUIPv6配置 Path MTU IPv4 对于数据过大的数据包会执行切片操作&#xff0c;但是切片有可能会造成设备性能的降低 IPv6使用Path MTU来传递数据过大的数据包 依次会协商最小的 MTU 单元为了减少中间转发设备的压力&#xff0c;中间转发设备不对 IPv6 报文进行分片…

【快速笔记】freeRTOS

第十八章 低功耗Tickless模式 睡眠模式:__WFI 中断唤醒 __WFE 事件唤醒 CPU CLK关闭 停止模式&#xff1a;RAM保持 中断唤醒 当 STM32F103 处于休眠模式的时候 Cortex-M3 内核停止运行&#xff0c;但是其他外设运行正常&#xff0c; 比如 NVIC、SRAM 等。 休眠模式的功耗比其他…

【Linux】环境部署kafka集群

目录 一、kafka简介 1. 主要特点 2.组件介绍 3.消息中间件的对比 二、环境准备 1.Java环境 2.Zookeeper环境 3.硬件环境集群 三、Zookeeper的集群部署 1.下载zookeeper 2.部署zookeeper集群 &#xff08;1&#xff09;node1节点服务器 &#xff08;2&#xff09;no…

排序----希尔排序

void ShellSort(int* a, int n) {int gap n;while (gap > 1){// 1保证最后一个gap一定是1// gap > 1时是预排序// gap 1时是插入排序gap gap / 3 1;for (size_t i 0; i < n - gap; i){int end i;int tmp a[end gap];while (end > 0){if (tmp < a[end]){…

6.C_数据结构_查询_哈希表

概述 哈希表的查询是通过计算的方式获取数据的地址&#xff0c;而不是依次比较。在哈希表中&#xff0c;有一个键值key&#xff0c;通过一些函数转换为哈希表的索引值。 其中&#xff1a;这个函数被称为哈希函数、散列函数、杂凑函数&#xff0c;记为&#xff1a;H(key) 哈希…

MySQL 中的锁定粒度:理解与应用

在 MySQL 数据库的使用中&#xff0c;锁定粒度是一个至关重要的概念。它决定了数据库在并发控制中锁定的范围和程度&#xff0c;对数据库的性能和并发能力有着深远的影响。今天&#xff0c;我们就来深入了解一下 MySQL 中的锁定粒度是什么意思&#xff0c;并通过实际案例来更好…