C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)

server/2024/11/27 15:00:27/

目录

题目: 无重复字符的最长子串

1. 题目解析

2. 算法原理

Ⅰ. 暴力枚举

Ⅱ. 滑动窗口(同向双指针)

3. 代码实现

Ⅰ. 暴力枚举

Ⅱ. 滑动窗口


题目: 无重复字符的最长子串

1. 题目解析

题目截图:

此题所说的子串与长度最小的子数组题目中所说的子数组相当于一个概念,都是数组中连续的一段,区别在于子串是在一个字符串中,子数组是在一个整数数组中。

 题目中要求找到一个没有重复字符的子串,并返回它的长度。

所以看到这里有三个长度为3的子串且是最长的,所以返回3。

这里可以看出全是b,只能找到一个字符,并返回长度1,因为再往后扩展也是重复的了。

 所以,这道题的要求,并要求返回什么如上面所示。

2. 算法原理

这道题也有两种解法的:

  1. 暴力枚举
  2. 滑动窗口 

Ⅰ. 暴力枚举

暴力枚举也就是把所有子串都枚举出来,枚举子串的时候,以某一个位置为起点,然后向后枚举,再接着以另一个某一个位置为起点,再向后枚举,这里注意的是枚举并不是全部枚举,而是当枚举一个位置时,继续再向后枚举的时候,如果发现有重复的字符出现,那么就停止枚举,也就是说,以这个位置开头的只能枚举到这里了,然后统计长度,接下来才是枚举第二个开头的,一直把所有情况枚举到,最后找一个长度的最大值。 

这里的长度为2。 

这里长度为3,通过上面题目解析中已经得知这里最长长度就是3. 

固定每一个有可能的起始位置,然后依次向后扩展,直到扩展不能扩展为止,然后统计一下长度,把所有情况都枚举到,再统计一下最大值。
 

不过这里我们都是通过肉眼观察它们是否有重复的,但是程序中可不会直接就能观测到,这里就需要处理是否重复的细节问题了,可以借助开放定址法的hash表来解决重复问题,只需要把对应的字母映射到表上(遍历一个字符就让它映射到哈希表上),然后表里字母对应位置的数据统计它出现的次数,若是大于 1,那么它就重复。

所以这里的解法:暴力枚举+哈希表(表的数据存储的是字符出现的次数,为了判断字符是否重复出现)

这里时间复杂度:O(N²)。 

所以,暴力枚举:

 先判断 right 指向的字符在不在 hash表 里(也就是看表里对应的数据是不是大于 0),不在就放进去,然后 right 向后移动,再接着判断 right 指向的元素在不在,不在就放进去,right 再向后移动,重复上面的操作(不在就在表里对应的位置 +1,然后 right 后移)

再接着判断重复上面操作: 

判断不在,重复上面操作: 

此时判断不在,重复上面操作,这时right指向了第二个字符a:

当上面right 到 a 时,字符先前已经存在于哈希表里了,这时候就要停止枚举操作,"deabc"的长度就是以d开头的能得到无重复字符串的子串的最长长度。

接下来,让left换一个位置,left后移动一位,此时再让right回退至left的位置:

接下来继续重复上面的操作,直到把所有符合条件的情况枚举出来,统计长度,再获取最长的那个长度返回即可。 

Ⅱ. 滑动窗口(同向双指针)

在上面暴力枚举种,我们发现有些情况是可以优化的,我们发现如下的情况:

这时left再往下一个移动的时候,这时到了字符a,让right回退到left,再继续枚举发现还是会到同一个a位置停止枚举,当left跳过了第一个出现的字符a之后,停止枚举的位置就发生变化了:

也就是说:

并且这里发现,在这个区间内依次往后枚举起始位置的话,因为终点是同样的,这时子串的长度就会递减,因此就可以让right不要拐回去了,让right在该位置先不动,先调整left,让left跳过有重复的字符:

暴力解法中,left移动到新的位置的时候,right就要回退至left的同位置,但这里也可以有个优化,也就是left跳过重复字符后,right是没有必要回退至left的:

 因此,我们发现这里left和right的移动方向是一致的,也就是同向双指针:

这里的窗口就是left和right区间内维护的无重复字符的子串,让字符先进入窗口,然后判断,当有重复的字符的时候就让它出窗口:

 这里的解法就是:利用规律,使用滑动窗口来解决问题。

注意:上面的情况每次都要更新结果,结果就是字符串的子串的长度。

规律:

  1. 当里面有重复的字符的时候,让left先向右移动,把出现的重复字符的位置之前的字符给跳过(因为它们的最后终点都会为这个重复的字符的第二次出现的位置)。
  2. 当left到符合要求的位置时候,right是不用回退的,可以继续向后移动,扩展该区间。

所以这里就可以同上一道题 长度最小的子数组 用滑动窗口的方法步骤解决:

  1. 先定义 left 和 right 并都初始化为0,充当窗口的左右端点。
  2. 进窗口(这里让字符串进入哈希表即可)。
  3. 判断:当窗口里有重复的字符时候,就要出窗口(就是从哈希表中删除该字符,注意:在删除之后,要再继续判断,直到没有重复字符为止)。
  4. 进出窗口都需要更新结果(符合要求的子串的长度,取新旧结果中最大的那一个即可)。
  5. 直到right指向最右边为止就就结束了。

 这里时间复杂度情况也同于  长度最小的子数组 的情况,根据实际情况是每一步操作仅仅会让 right 向右移动 1 位或 left 向右移动 1 位,直到 right 移动到最后的位置。最坏的情况就是两个指针都遍历了一遍该数组,也就是2n次,所以时间复杂度为:O(N)。

接下来实现两种方法的代码:

3. 代码实现

题目链接:无重复字符的最长子串

Ⅰ. 暴力枚举

时间复杂度:O(N²)。

//暴力枚举
class Solution {
public:int lengthOfLongestSubstring(string s) {int n = s.size();int len = 0;for(int left = 0; left < n; left++)  //先固定起始位置{int hash[128] = {0};  //将字符串中的字符出现次数映射到hash表for(int right = left; right < n; right++)//依次从left位置向后枚举扩展区间{hash[s[right]]++;//让字符充当下标,该位置字符出现,就让它对应的hash值+1if(hash[s[right]] > 1)  //大于1说明出现重复字符了{break;      //直接退出循环}len = max(len, right - left + 1);   //更新结果}}return len;}
};

提交记录:

Ⅱ. 滑动窗口

时间复杂度:O(N)。

//滑动窗口
class Solution {
public:int lengthOfLongestSubstring(string s) {int n = s.size();int len = 0;int hash[128] = { 0 };   //使用数组模拟hash表//遇到重复的不需要让right回退了,让left跳过重复的字符之后,再处理right即可for(int left = 0, right = 0; right < n; right++)  {hash[s[right]]++;  //进入窗口while(hash[s[right]]>1) //判断{//大于1说明出现重复了hash[s[left++]]--;    //出窗口}len = max(len,right-left+1);  //更新结果         }return len;}
};

提交记录:

制作不易,若有不足之处或出问题的地方,请各位大佬多多指教 ,感谢大家的阅读支持!!!   


http://www.ppmy.cn/server/145360.html

相关文章

调大Vscode资源管理器字体

对于调整资源管理器字体大小&#xff08;也就是下图红框&#xff09;&#xff0c;查找了网上很多方法。要么介绍的方法是调整了代码字体&#xff0c;要么是调节了终端字体&#xff0c;要么是通过整体放缩实现的调整&#xff0c;总之都不合适。 唯一的调整方法是在几篇CSDN里看到…

【计算机网络】C/C++实现解析Wireshark离线数据包,附源码

直接先上demo 以下是一个完整的示例代码&#xff0c;演示如何使用 pcap_open_offline 函数打开一个捕获文件并读取数据包。 #include <stdio.h> #include <pcap.h>int main(int argc, char **argv) {if (argc ! 2) {fprintf(stderr, "Usage: %s <capture…

c++编程玩转物联网:使用芯片控制8个LED实现流水灯技术分享

在嵌入式系统中&#xff0c;有限的GPIO引脚往往限制了硬件扩展能力。74HC595N芯片是一种常用的移位寄存器&#xff0c;通过串行输入和并行输出扩展GPIO数量。本项目利用树莓派Pico开发板与74HC595N芯片&#xff0c;驱动8个LED实现流水灯效果。本文详细解析项目硬件连接、代码实…

Python软体中如何实现一个单向链表的反转功能:详解与复杂度分析

Python软体中如何实现一个单向链表的反转功能:详解与复杂度分析 引言 链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据和一个指向下一个节点的引用(或指针)。在算法和数据结构的学习中,反转单向链表是一个经典问题,考察了程序员对链表操作的理解和实现…

游戏引擎学习第23天

实时代码编辑功能的回顾 当前实现的实时代码编辑功能已经取得了显著的成功&#xff0c;表现出强大的性能和即时反馈能力。该功能允许开发者在修改代码后几乎立即看到变化在运行中的程序中体现出来&#xff0c;极大提升了开发效率。尽管目前的演示内容较为简单&#xff0c;呈现…

Redis主从复制+哨兵集群搭建

哨兵模式 什么是哨兵模式 主从切换技术的方法是&#xff1a;当主服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;这就需要人工干预&#xff0c;费事费力&#xff0c;还会造成一段时间内服务不可用。这不是一种推荐的方式&#xff0c;更多时候&…

网站布局编辑器前端开发:设计要点与关键考量

一、设计说明 &#xff08;一&#xff09;功能模块 可视化操作区域 这是用户进行网站布局设计的主要画布。通过拖放各种页面元素&#xff08;如文本框、图片、按钮、导航栏等&#xff09;到该区域&#xff0c;用户能够直观地构建网站页面的布局结构。支持对元素的实时缩放、旋…

【设计模式】【结构型模式(Structural Patterns)】之组合模式(Composite Pattern)

1. 设计模式原理说明 组合模式&#xff08;Composite Pattern&#xff09; 是一种结构型设计模式&#xff0c;它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性。这意味着无论处理的是单一对象还是复合对象…