滑动窗口(2)——哈希表辅助的滑动窗口算法

欢迎来到博主的专栏:算法解析
博主ID:代码小豪

文章目录

    • leetcode438——找到字符串中所有字母异位词
      • 题目解析
      • 算法原理
      • 题解代码
    • leetcode30——串联所有单词的子串
      • 题目解析
      • 算法原理
      • 题解代码

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

题目解析

在这里插入图片描述
异位词是指,有相同的英文字母组成的单词,不要求单词中的字母顺序一致。

以示例1为例:
p中的词组是"abc"。而在s当中有"cba","bac"这两个子串,满足异位词的条件,因此返回这两个子串的起始下标位置。

如果s=“cbaebabacd”,p = “abba”。则s中存在子串"baba"满足异位词的条件。
在这里插入图片描述

返回子串"baba"在s当中的起始下标位置4。

算法原理

首先,我们思考一下,如何证明子串和字符串t的是属于字母异位词呢?我们要找到它们的特点,即字母要对应,字母的个数要相同,比如t当中有3个a,1个b,2个c,那么子串也要满足有3个a,1个b,2个c,不能存在更多的字母,比如d,也不能出现不同的个数,比如5个a。

那么我们可以用哈希表来完成字母与个数的映射。我们可以创建哈希表1,用来映射t中的字母。
在这里插入图片描述
因为小写字母一共有26位,我们可以用int [26]的数组来作为哈希表,而不是STL中的unordered_map。因为STL容器虽然功能强大,但是效率会比数组低一些。

接下来遍历出整个s中所有大小为4的子串,并且子串中出现的字母映射到另一张哈希表2中。
在这里插入图片描述
接下来将hash2出现的结果与hash1挨个作对比,若hash1与hash2一致,则说明该子串是字母异位词。此时将该下标位置记录下来。
在这里插入图片描述
因此我们需要找到一个遍历字符串的算法。我们观察一下上面遍历的结果。
在这里插入图片描述
由于子串的长度是固定的,可以发现,如果我们从左往右遍历出所有的子串,只需要删除前一个元素,插入后一个元素即可(橙色为删除的元素,蓝色为插入的元素)。因此我们可以采用滑动窗口的遍历方式(见上一篇文章,滑动窗口是一种由暴力枚举优化而来的遍历算法,可以将遍历的复杂度从O(N^2)变成O(N))。

因此我们可以定义出一个left指针,和right指针,使其都指向字符串的起始位置。
在这里插入图片描述
由于我们还使用了哈希表来辅助完成对比任务,因此我们要让right当前指向的元素,插入哈希表中。这步操作称为进窗口
在这里插入图片描述

当子串的长度等于t的长度时,我们要判断一下hash1与hash2是否相等,相等就说明子串与t构成异位词。记录下left的当前下标(子串的起始位置)。

由于子串需要和字符串t的大小一致(异位词的定义)。因此我们要保持right-left+1的大小不超过t的字符串长度,在本例中,字符串t为"abab",即长度为4.因此当right-left+1大于4时,我们要让left指向的元素,在哈希表中被删除,该操作称为出窗口,完成出窗口操作后,令left++,如下:
在这里插入图片描述
无论是进窗口,还是出窗口,都有可能导致子串的长度与t的长度相等,因此无论是进窗口操作完成后,还是出窗口操作完成后,都要判断一下hash1和hash2。

最后就是判断hash1和hash2的方法了,我们可以通过同时遍历hash1和hash2的方式来完成,但是这么做效率其实并不高,博主这里讲解一个优化的算法,优化的方面就是简化哈希表的判断。

我们可以使用count来记录当前子串中的有效字符个数。有效字符指的是当前子串符合构成t的异位词的字符。比如t为"aacb",子串为"cccb",此时有效字符个数仅为2(因为只有"cb"是符合构成异位词条件的字符),因此不构成异位词。当count与t的个数相等时,此时才符合异位词的条件。

那么如何计算count的值呢?若是hash2中记录的该字符的计数小于等于hash1中该字符的计数。则说明该字符是一个有效字符。若是进窗口的字符是有效字符,则count++,若是出窗口的字符符合有效字符,则count–。

因此整体的运行逻辑如下:
1.进窗口并判断需要更新有效字符
2.判断是否要出窗口,若出窗口则要更新有效字符
3.判断是否构成异位词
4.right++。

题解代码

class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> ret;int n=p.size();int hash1[26];for(auto e:p){hash1[e-'a']++;}int hash2[26]={0};for(int left=0,right=0,count=0;right<s.size();right++){//进窗口char in=s[right];hash2[in-'a']++;if(hash2[in-'a']<=hash1[in-'a']) count++;//判断有效字符//出窗口if(right-left+1>n){char out=s[left++];if(hash2[out-'a']<=hash1[out-'a']) count--;//判断有效字符hash2[out-'a']--;}if(count==n) ret.push_back(left);//判断是否为异位词}return ret;}
};

leetcode30——串联所有单词的子串

题目解析

在这里插入图片描述
这道题的难度确实符合困难,即使是博主在思路非常明确的情况下做这道题依然被许多细节给困扰一段时间。

算法原理

这道题我们可以看做是找到字符串中所有字母异位词的plus版,为什么这么说呢?我们以示例1为例:
在这里插入图片描述
如果我们让words[0]视为’A’,words[1]视为’b’。未出现在words中的其他字符串等于其他字母,那么s和words会变成下面这样:
在这里插入图片描述
有没有发现,这道题的解决思路和异位词的解决思路一模一样?没错,确实如此,我们一样时创建两个哈希表,只不过哈希表中不再是字符与计数的映射关系,而是字符串和计数的映射关系,count从有效字符的个数变成了有效字符串的个数,遍历的方式依然是滑动窗口,这么想这道题的难度是不是从困难变成和异位词一样的一般了?

不过我们要注意几个细节

细节1:字符串s有多种遍历方式
在这里插入图片描述
解决方法:分多次从不同的起始位置遍历

细节2:
right和left每次移动都要跳向后多个元素,因为这次滑动窗口要遍历的不是单个字符,而是定长的字符串

而且由于此题涉及较多的字符串操作,因此要求我们对STL库有一定的熟练度。

题解代码

class Solution {
public:vector<int> findSubstring(string s, vector<string>& words) {vector<int> ret;unordered_map<string,int> hash1;int len=words[0].size();for(auto& e:words){hash1[e]++;}for(int i=0;i<len;i++){//多次遍历unordered_map<string,int> hash2;int all=0;//hash2中记录的字符串总个数int cnt=0;//有效字符串总数for(int left=i,right=left;right+len<=s.size();right+=len){//进窗口string sub=s.substr(right,len);hash2[sub]++;all++;if(hash2[sub]<=hash1[sub]) cnt++;//出窗口if(all>words.size()){string out;out=s.substr(left,len);if(hash2[out]<=hash1[out]) cnt--;hash2[out]--;left+=len;all--;}if(cnt==words.size()) ret.push_back(left);}}return ret;}
};

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

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

相关文章

Deepseek -> 如何写 Dockerfile

嗯&#xff0c;用户问的是如何制作Dockerfile&#xff0c;我得先理清楚步骤。首先&#xff0c;Dockerfile的基础结构是什么&#xff1f;应该从基础镜像开始&#xff0c;对吧&#xff1f;比如FROM指令。然后可能需要设置工作目录&#xff0c;用WORKDIR。接着复制文件&#xff0c…

RabbitMQ重复消费如何解决

消息重复消费的原因 生产者重试&#xff1a;网络波动导致生产者未收到 Broker 确认&#xff0c;重复发送消息。消费者失败&#xff1a;消费者处理消息后未发送 ACK&#xff0c;消息重新入队。集群故障转移&#xff1a;主节点宕机&#xff0c;未确认消息被重新投递。 解决方案 …

Node-RED基础1

目录 一、概述二、安装三、基操四、通讯五、数据六、节点七、 应用END 一、概述 Rode-Red是什么&#xff1f; 基于Node.js的物联网开发工具&#xff0c;做API、通讯&#xff1b;提供了一些基本的监控功能&#xff0c;可在编辑器界面中查看节点的运行状态、消息流量等信息。通…

java登神之阶之顺序表

一、了解List接口 在Java中&#xff0c;List接口是一个非常重要的集合框架接口&#xff0c;它继承自Collection接口&#xff08;Collection接口继承Iterable接口&#xff09;。List接口定义了一个有序集合&#xff0c;允许我们存储元素集合。并且可以根据元素的索引来访问集合中…

redux_旧版本

reduxjs/toolkit&#xff08;RTK&#xff09;是 Redux 官方团队推出的一个工具集&#xff0c;旨在简化 Redux 的使用和配置。它于 2019 年 10 月 正式发布&#xff0c;此文章记录一下redux的旧版本如何使用&#xff0c;以及引入等等。 文件目录如下&#xff1a; 步骤 安装依…

MySQL:SQL优化实际案例解析(持续更新)

文章目录 一、MySQL&#xff1a;SQL优化1、时间格式化问题&#xff08;字符串&#xff09;2、in/inner join的问题 一、MySQL&#xff1a;SQL优化 1、时间格式化问题&#xff08;字符串&#xff09; -- 优化前 SELECT * FROM test_table WHERE date_format( begin_time, %Y-%…

【含文档+PPT+源码】基于Python的美食数据的设计与实现

项目介绍 本课程演示的是一款基于Python的美食数据分析系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该项目附带的源码…

vue调整表格样式之深度修改

举例&#xff1a; <div class"grid-item"><h3>日数据</h3><el-table :data"dailyData" v-loading"loading"><el-table-column label"销售姓名" align"center" prop"salesName" />…

【Go每日一练】统计字符出现的次数

&#x1f47b;创作者&#xff1a;丶重明 &#x1f47b;创作时间&#xff1a;2025年3月9日 &#x1f47b;擅长领域&#xff1a;运维 目录 1.&#x1f636;‍&#x1f32b;️题目&#xff1a;统计字符出现的次数2.&#x1f636;‍&#x1f32b;️代码中可用的资源3.&#x1f636;…

uniapp在APP平台(Android/iOS)选择非媒体文件

TOC 背景 在我们APP开发过程中&#xff0c;经常会有这样一个需求场景&#xff1a;从手机中选择文件然后进行上传&#xff0c;这些文件主要分为两类&#xff0c;媒体文件和非媒体文件。而媒体文件选择在APP平台我们可以使用uni.chooseImage和uni.chooseVideo这两个API来实现。…

【eNSP实战】配置交换机端口安全

拓扑图 目的&#xff1a;让交换机端口与主机mac绑定&#xff0c;防止私接主机。 主机PC配置不展示&#xff0c;按照图中配置即可。 开始配置之前&#xff0c;使用PC1 ping 一遍PC2、PC3、PC4、PC5&#xff0c;让交换机mac地址表刷新一下记录。 LSW1查看mac地址表 LSW1配置端…

卡尔曼滤波算法从理论到实践:在STM32中的嵌入式实现

摘要&#xff1a;卡尔曼滤波&#xff08;Kalman Filter&#xff09;是传感器数据融合领域的经典算法&#xff0c;在姿态解算、导航定位等嵌入式场景中广泛应用。本文将从公式推导、代码实现、参数调试三个维度深入解析卡尔曼滤波&#xff0c;并给出基于STM32硬件的完整工程案例…

Redis----大key、热key解决方案、脑裂问题

文章中相关知识点在往期已经更新过了&#xff0c;如果有友友不理解可翻看往期内容 出现脑裂问题怎么保证集群还是高可用的 什么是脑裂问题 脑裂说的就是当我们的主节点没有挂&#xff0c;但是因为网络延迟较大&#xff0c;然后和主节点相连的哨兵通信较差&#xff0c;之后主…

python总结(3)

创建自定义类 终于要创建自定义类了!下面是一个简单的示例: class Person:def set_name(self, name):self.name namedef get_name(self):return self.namedef greet(self):print("Hello, world! Im {}.".format(self.name))这个示例包含三个方法定义&#xff0c;它…

word毕业论文“et al.”替换为“等”——宏

Sub 中文参考文献改等()中文参考文献改等 宏Selection.Find.ClearFormattingSelection.Find.Replacement.ClearFormattingWith Selection.Find.Text "([一-龥], )et al.".Replacement.Text "\1等.".Forward True.Wrap wdFindContinue.Format False.Ma…

网络编程-----服务器(多路复用IO 和 TCP并发模型)

一、单循环服务器模型 1. 核心特征 while(1){newfd accept();recv();close(newfd);}2. 典型应用场景 HTTP短连接服务&#xff08;早期Apache&#xff09;CGI快速处理简单测试服务器 3. 综合代码 #include <stdio.h> #include <sys/types.h> /* See NO…

typora高亮方案+鼠标侧键一键改色

引言 在typora里面有一个自定义的高亮, <mark></mark>>但是单一颜色就太难看了, 我使用人工智能, 搜索全网艺术家, 汇集了几种好看的格式,并且方便大家侧键一键 调用, 是不是太方便啦 ! 示例 午夜模式 春意盎然 深海蓝调 石墨文档 秋日暖阳 蜜桃宣言 使用方法 …

swift -(5) 汇编分析结构体、类的内存布局

一、结构体 在 Swift 标准库中&#xff0c;绝大多数的公开类型都是结构体&#xff0c;而枚举和类只占很小一部分 比如Bool、 Int、 Double、 String、 Array、 Dictionary等常见类型都是结构体 ① struct Date { ② var year: Int ③ var month: Int ④ …

从零开始学机器学习——初探分类器

首先给大家介绍一个很好用的学习地址:https://cloudstudio.net/columns 在上一节中,我们已经完成了对所有经过均衡和清洗的菜品数据的准备工作。接下来,我们将考虑使用多种算法,也就是不同的分类器,来构建模型。我们将利用这个数据集和各种分类器,基于一组特定的配料,预…

黑色RGB是什么

>好的&#xff0c;用户现在问的是“黑色rgb是什么”。我需要先理解用户的问题。之前用户在使用MATLAB调整网格线颜色时&#xff0c;可能看到了默认颜色是黑色&#xff0c;或者之前我提到过默认颜色是[0.15 0.15 0.15]&#xff0c;而用户可能现在想知道黑色的RGB值具体是什么…