[Algorithm][滑动窗口][水果成篮][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

news2025/2/26 8:12:58

目录

  • 1.水果成篮
    • 1.题目链接
    • 2.算法原理讲解
    • 3.代码讲解
  • 2.找到字符串中所有字母异位词
    • 1.题目链接
    • 2.算法原理讲解
    • 3.代码实现
  • 3.串联所有单词的字串
    • 1.题目链接
    • 2.算法原理讲解
    • 3.代码实现
  • 3.最小覆盖字串
    • 1.题目链接
    • 2.算法原理讲解


1.水果成篮

1.题目链接

  • 水果成篮

2.算法原理讲解

  • 研究的对象是⼀段连续的区间,可以使⽤**「滑动窗⼝」**思想来解决问题
    • 将问题转化为:找出一个最长的子数组长度,子数组中不超过两种类型的水果
  • 让滑动窗⼝满⾜:窗⼝内⽔果的种类只有两种
  • 做法
    • 右端⽔果进⼊窗⼝的时候,⽤哈希表统计这个⽔果的频次
    • 这个⽔果进来后,判断哈希表的⼤⼩
      • 如果⼤⼩超过2:说明窗⼝内⽔果种类超过了两种
        • 那么就从左侧开始依次将⽔果划出窗⼝,直到哈希表的⼤⼩⼩于等于2,然后更新结果
      • 如果没有超过2:说明当前窗⼝内⽔果的种类不超过两种,直接更新结果ret
  • 优化
    • v1.0中,哈希表有大量的插入删除
    • v2.0中,由于水果类型最大值是一个定值,所以可以把hash数组写死,这样以空间换时间,不会存在哈希冲突
      • 此时不用STL容器,无法 O ( 1 ) O(1) O(1)判断水果种类数量,此时添加一个变量kinds即可
        请添加图片描述

3.代码讲解

// v1.0
// 模型转化为:数组只有两个元素的最大子数组
int TotalFruit(vector<int>& fruits) 
{
    int ret = 0;
    unordered_map<int, int> basket; // <水果种类 数量>

    for(int left = 0, right = 0; right < fruits.size(); right++)
    {
        basket[fruits[right]]++; // 入窗口

        while(basket.size() > 2) // 判断
        {
            // 出窗口
            basket[fruits[left]]--;

            if(basket[fruits[left]] == 0)
            {
                basket.erase(fruits[left]);
            }

            left++;
        }

        ret = max(ret, right - left + 1); // 更新数据
    }

    return ret;
}
---------------------------------------------------------------
// v2.0 优化
// 改造哈希表,以改善时间复杂度
int TotalFruit(vector<int>& fruits) 
{
    int ret = 0, kinds = 0;

    // 水果类型小于一个定值
    // 以空间换时间
    int basket[100001] = { 0 };

    for(int left = 0, right = 0; right < fruits.size(); right++)
    {
        if(basket[fruits[right]] == 0)
        {
            kinds++;
        }
        basket[fruits[right]]++; // 入窗口

        while(kinds > 2) // 判断
        {
            // 出窗口
            basket[fruits[left]]--;

            if(basket[fruits[left]] == 0)
            {
                kinds--;
            }

            left++;
        }

        ret = max(ret, right - left + 1); // 更新数据
    }

    return ret;
}

2.找到字符串中所有字母异位词

1.题目链接

  • 找到字符串中所有字母异位词

2.算法原理讲解

  • 思路:「滑动窗⼝」 + 「哈希表」
  • 做法
    • 因为字符串p的异位词的⻓度⼀定与字符串p的⻓度相同
      • 所以可以在字符串s中构造⼀个⻓度为与字符串p的⻓度相同的滑动窗⼝,并在滑动中维护窗⼝中每种字⺟的数量
    • 当窗⼝中每种字⺟的数量与字符串p中每种字⺟的数量相同时,则说明当前窗⼝为字符串 p的异位词
    • 因此可以⽤两个⼤⼩为26的数组来模拟哈希表
      • ⼀个来保存s中的⼦串每个字符出现的个数
      • 另⼀个来保存p中每⼀个字符出现的个数
      • 这样就能判断两个串是否是异位词
        ![[Pasted image 20240326190744.png]]
  • 优化:更新结果的判断条件 -> 利用count来统计窗口中"有效字符"的个数
    • 进窗口:进入后 -> if (hashS[in] <= hashP[in]) -> count++
    • 出窗口:出去前 -> if (hashS[out] <= hashP[out]) -> count--
    • 更新结果:count == len

3.代码实现

// 将问题转化为:p中的字母出现的次数与s中某个字串中出现的次数相同
// v1.0
vector<int> FindAnagrams(string s, string p) 
{
    vector<int> ret;
    int hashS[26] = { 0 };
    int hashP[26] = { 0 };

    // 将p如hash
    for(auto& ch : p)
    {
        hashP[ch - 'a']++;
    }

    // 处理s
    int len = p.size();
    for(int left = 0, right = 0; right < s.size(); right++)
    {
        hashS[s[right] - 'a']++; // 入窗口

        if(right - left + 1 > len) // 判断窗口是否大了
        {
            hashS[s[left++] - 'a']--; // 出窗口
        }

        if(right - left + 1 == len) // 尝试更新数据
        {
            bool flag = true;
            for(int i = 0; i < 26; i++)
            {
                if(hashP[i] != hashS[i])
                {
                    flag = false;
                    break;
                }
            }

            if(flag)
            {
                ret.push_back(left);
            }
        }
    }

    return ret;
}
-----------------------------------------------------------------
// v2.0 优化更新结果的判断条件,不用每次都遍历hash了
vector<int> findAnagrams(string s, string p) 
{
    vector<int> ret;
    int hashS[26] = { 0 };
    int hashP[26] = { 0 };

    // 将p如hash
    for(auto& ch : p)
    {
        hashP[ch - 'a']++;
    }

    // 处理s
    int count = 0, len = p.size();
    for(int left = 0, right = 0; right < s.size(); right++)
    {
        // 入窗口 + 维护count
        if(++hashS[s[right] - 'a'] <= hashP[s[right] - 'a'])
        {
            count++; // 入窗口的字符是一个有效字符
        }

        if(right - left + 1 > len) // 判断窗口是否大了
        {
            // 维护count
            if(hashS[s[left] - 'a']-- <= hashP[s[left] - 'a'])
            {
                count--; // 出窗口的元素是一个有效字符
            }

            left++; // 出窗口
        }

        if(count == len) // 更新结果
        {
            ret.push_back(left);
        }
    }

    return ret;
}

3.串联所有单词的字串

1.题目链接

  • 串联所有单词的字串

2.算法原理讲解

  • 如果把每⼀个单词看成⼀个字⺟,问题就变成了找到**「字符串中所有的字⺟异位词」**
    • ⽆⾮就是之前处理的对象是⼀个⼀个的字符,这⾥处理的对象是⼀个⼀个的单词
  • 注意的点 && 区别
    • 哈希表:hash<string, int>
    • left指针与right指针的移动
      • 移动的步长,是单词的长度len -> 一次跨过一个单词
    • 滑动窗口执行的次数:len
      • 因为无法确定起点是在哪儿,所以要执行len
      • 大于len次则没必要,因为第一次与第len+1次是重复的情况…
        请添加图片描述

3.代码实现

vector<int> findSubstring(string s, vector<string>& words) 
{
    int len = words[0].size();
    unordered_map<string, int> mapS;
    unordered_map<string, int> mapV;
    vector<int> ret;

    for(auto& str : words)
    {
        mapV[str]++;
    }

    for(int i = 0; i < len; i++) // 滑动窗口的执行次数
    {
        int count = 0;
        for(int left = i, right = i; right + len <= s.size(); right += len)
        {
            // 入窗口,截取子串,维护count
            string str = s.substr(right, len);
            mapS[str]++;

            // 看看mapV中是否存在str,以避免插入不需要的值
            if(mapV.count(str) && mapS[str] <= mapV[str]) 
            {
                count++;
            }

            // 判断窗口是否大了
            if(((right - left + len) / len) > words.size())
            {
                // 维护count
                string tmp = s.substr(left, len);
                if(mapV.count(tmp) && mapS[tmp]-- <= mapV[tmp])
                {
                    count--;
                }

                left += len; // 出窗口
            }

            if(count == words.size()) // 更新结果
            {
                ret.push_back(left);
            }
        }
        mapS.clear();
    }

    return ret;
}

3.最小覆盖字串

1.题目链接

  • 最小覆盖字串

2.算法原理讲解

  • 研究对象是连续区间,因此可以尝试使⽤「滑动窗⼝」的思想来解决

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

    • 可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝ 内字符串的信息

    • 当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案

      请添加图片描述

  • 优化:判断条件 -> 使用变量count标记有效字符的种类
    请添加图片描述

string MinWindow(string s, string t) 
{
    // 仅用数组可以避免STL的开销,提高效率
    int hashS[128] = { 0 };
    int hashT[128] = { 0 };

    int kinds = 0;
    for(auto& ch : t)
    {
        if(hashT[ch]++ == 0)
        {
            kinds++;
        }
    }

    int begin = -1, minLen = INT_MAX;
    for(int left = 0, right = 0, count = 0; right < s.size(); right++)
    {
        // 入窗口
        char in = s[right];

        // 维护count,仅统计t中有效字符的种类
        if(++hashS[in] == hashT[in])
        {
            count++;
        }

        while(count == kinds) // 判断
        {   
            // 更新
            if(right - left + 1 < minLen)
            {
                begin = left;    
                minLen = right - left + 1;
            }

            // 出窗口 && 维护count
            char out = s[left++];
            if(hashS[out]-- == hashT[out])
            {
                count--;
            }
        }
    }

    string ret = "";
    if(begin != -1)
    {
        ret = s.substr(begin, minLen);    
    }

    return ret;
}

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

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

相关文章

Docker in Docker的原理与实战

Docker in Docker&#xff08;简称DinD&#xff09;是一种在Docker容器内部运行另一个Docker实例的技术。这种技术允许用户在一个隔离的Docker容器中创建、管理和运行其他Docker容器&#xff0c;从而提供了更灵活和可控的部署选项。以下是DinD的主要特点&#xff1a; 隔离性&am…

【工厂模式】工厂方法模式、抽象工厂模式-简单例子

简单工厂模式&#xff0c;请跳转到我的另一篇博客【工厂模式】简单工厂模式-简单例子-CSDN博客 四、工厂方法模式 &#xff08;1&#xff09;这部分还是不变&#xff0c;创建一个Car接口&#xff0c;和两个实现类。 public interface Car {void name(); }public class WuLing…

文献补充材料怎么查找获取

最近很多同学求助外文文献补充材料&#xff0c;有的同学在学校提供的资源中能找到全文却无法下载文献的补充材料&#xff0c;这是因为该校只收录了全文而没有收录补充材料。那么文献补充材料怎么查找又如何获取呢&#xff1f;请看本文的经验分享&#xff1a; 1、文献的补充材料…

在Linux系统中搜索当前路径及其子目录下所有PDF文件中是否包含特定字符串

目录标题 方法一&#xff1a;pdfgrep方法二&#xff1a;使用find和xargs与pdftotext&#xff08;将PDF转换为文本&#xff09;组合&#xff0c;然后用grep搜索 方法一&#xff1a;pdfgrep pdfgrep -ri "rockchip" .方法二&#xff1a;使用find和xargs与pdftotext&am…

Yolov8项目实践——基于yolov8与OpenCV实现目标物体运动热力图

概述 在数据驱动和定位的世界中&#xff0c;对数据进行解释、可视化和决策的能力变得日益重要。这表明&#xff0c;使用正确的工具和技术可能是项目成功的关键。在计算机视觉领域&#xff0c;存在许多技术来解释从视频&#xff08;包括录像、流媒体或实时视频&#xff09;中获…

【HC32L110】华大低功耗单片机启动文件详解

本文主要记录华大低功耗单片机 HC32L110 的 汇编启动过程&#xff0c;包括startup_hc32l110启动文件详细注释 目录 1.启动文件的作用2.堆栈定义2.1 栈2.2堆 3.向量表4.复位程序5.中断服务程序6.堆栈初始化启动过程详解7.1从0地址开始7.2在Reset_Handler中干了啥&#xff1f; 8.…

ContextMenuStrip内容菜单源对象赋值学习笔记(含源码)

一、前言 MetroTileItem属于第三方控件,无法定义ContextMenuStrip属性 想实现某子项点击菜单时,与源控件(按钮metroTileItem)的某值对应,用于动态控制按钮的状态或方法 1.1 效果 二、实现方法 2.1 方法1 (代码,说明见注释) private void metroTileItem_MouseDown(o…

python_django中小学家校互动系统vue_flask家校联系

实现了一个完整的家校互动系统&#xff0c;其中主要有作业信息模块、学校管理员模块、学生学籍模块、学生成绩模块、学科模块、系统新闻模块、系统公告模块、校内新闻模块、校内公告模块、用户表模块、token表模块、关于我们模块、收藏表模块、年级模块、家长模块、教师模块、互…

openlayers学习(一)

首先感谢大佬们写的文章&#xff0c;博客链接已在文章最后贴出&#xff0c;在接下来的内容中&#xff0c;我将会引用其中的一些定义结论。 之前文章写过一个Arcgis api for js的小demo&#xff0c;openlayers项目代码就继续在写这个小demo框架上。 openlayers官网 初始化地图…

Flink学习(六)-容错处理

前言 Flink 是通过状态快照实现容错处理 一、State Backends 由 Flink 管理的 keyed state 是一种分片的键/值存储&#xff0c;每个 keyed state 的工作副本都保存在负责该键的 taskmanager 本地中。 一种基于 RocksDB 内嵌 key/value 存储将其工作状态保存在磁盘上&#x…

聚观早报 | 华为Pura70系列先锋计划;月之暗面升级Kimi

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 4月19日消息 华为Pura70系列先锋计划 月之暗面升级Kimi OPPO Find X7将推白色版本 波士顿动力推出人形机器人 v…

sql-labs(1-8关)

mysql数据结构 在练习靶场前我们需要了解以下mysql数据库结构&#xff0c;mysql数据库5.0以上版本有一个自带的数据库叫做information_schema,该数据库下面有两个表一个是tables和columns。tables这个表的table_name字段下面是所有数据库存在的表名。table_schema字段下是所有…

面试: Hashtable vs ConcurrentHashMap

一、Hashtable和ConcurrentHashMap的不同和相同点 Hashtable 与 ConcurrentHashMap 都是线程安全的Map 集合。Hashtable 并发度低&#xff0c;整个Hashtable对应一把锁&#xff0c;同一时刻&#xff0c;只能有一个线程操作它。1.8之前ConcurrentHashMap使用了Segment 数组&…

爱普生发布一款16位MCU产品用于大电流LED驱动

精工爱普生发布一款内置Flash存储器的16位微控制器S1C17M13 该新品可以提供最大56mA的驱动电流用于驱动发光二极管(LED) 以往爱普生的微处理器大多继承了液晶驱动器电路&#xff0c;但近来随着工业自动化和家用设备使用7段LED显示的数量大幅增加&#xff0c;爱普生也推出了对应…

pdf加水印怎么加?自己原创的PDF资料分享到网络上需要采取一些版权保护的措施,添加水印就是个不错的选择

一&#xff0c;水印的基本概念 水印通常是一种用于标识文件来源、版权信息或防止非法复制的标记。它可以是文字、图形或图像等形式&#xff0c;以半透明或半淡化的方式嵌入到文件中&#xff0c;既不影响文件的正常阅读&#xff0c;又能起到标识和保护的作用。 二&#xff0c;…

如何查看Debian Linux的内核版本

2024年4月19日&#xff0c;周五上午 uname -r

测试数据整理--chatgpt 构造sql语句导出数据库数据

在测试过程中&#xff0c;我们有时候需要准备一些测试数据&#xff0c;若从系统中直接导出Excel数据&#xff0c;数据往往庞大且需要整合&#xff0c;不好整理&#xff0c;于是我们直接去数据库中查询一些表&#xff0c;数据整合后直接导出结果会更方便。 我们今天就 用 chatg…

【EdgeBox-8120AI-TX2】Ubuntu18.04 + ROS_ Melodic + 星秒PAVO2单线激光 雷达评测

大家好&#xff0c;我是虎哥&#xff0c;好久不见&#xff0c;最近这断时间出现了一点变故&#xff0c;开始自己创业&#xff0c;很多事需要忙&#xff0c;所以停更了大约大半年&#xff0c;最近一切已经理顺&#xff0c;所以我还是抽空继续我之前的FLAG&#xff0c;CSDN突破十…

矩阵混乱度(熵值)代码计算

1、先回顾下熵值的数据公式&#xff1a; 2、jax.numpy代码 注意的点&#xff1a;熵值计算的输入的必须是归一化的正值 import jax.numpy as jnp import jax def _entroy(probs):log_probs jnp.log2(jnp.maximum(1.0e-30, probs))mean_sum_plogp jnp.mean(- jnp.sum(log_pro…

OpenHarmony鸿蒙南向开发案例:【智能窗户通风设备】

样例简介 本文档介绍了安全厨房案例中的相关智能窗户通风设备&#xff0c;本安全厨房案例利用轻量级软总线能力&#xff0c;将两块欧智通V200Z-R/BES2600开发板模拟的智能窗户通风设备和燃气告警设备组合成。当燃气数值告警时&#xff0c;无需其它操作&#xff0c;直接通知软总…