【用人话讲算法】leetcode无重复字符的最长子串

news2024/11/24 8:37:31

【用人话讲算法】leetcode无重复字符的最长子串

文章目录

  • 【用人话讲算法】leetcode无重复字符的最长子串
    • 题目
    • 简单思路(暴力)
    • 优化思考
    • 怎么写代码?怎么到下一个?
      • while
      • for
    • 思路总结
    • while和for循环总结

题目

在这里插入图片描述
题目的意思是说,从一串字符中找到一个子字符串,这个子字符串中的字符需要全部都是不相同的,求解这样的字符串中,长度最长的那个子字符串的长度是多少。

简单思路(暴力)

我们先想想,作为人类,我们会怎么解决这个问题。
首先因为并不能确定这个子字符串是从哪里开头的,因此我们会从每个字符都开头一次,试一试。当碰到一个字符和已经存在的串里面的数字相同的时候,我们就可以知道当这个数字开头的时候,最长的长度是多少,接着就可以比较所有数字开头的时候找到的最大的串里面最大的,我们就可以知道这个最长的子串的长度了。

优化思考

但是这样的解法是最优解吗?时间复杂度是O(n2)。需要回答这个问题,我们需要看看有没有哪些工作是可以不做的。

比如这样一个串:abcdeab
当a作为开头的字母的时候,我们会找到这样的串,
abcde然后换为b开头,我们找到的串为
bcdea,找这两个串的时候,比较每个数字的时候,过程其实是一模一样的,有没有什么办法可以记录下来这样的事情?

作为人类我们可以想到如下的办法:
在这里插入图片描述
看起来也很有道理是不是!但是怎么让计算机知道呢?
也许我们可以把这个子串记录下来,毕竟我们总是在拿空间换时间的。然后当我们指到下一个的时候,只要在记录里面,我们就在这个记录里面删除前面那些数字,直到找到那个切断的数字之后,切断前面的,就可以继续的往后找了。

上面的过程是不是就和我们人的思维很接近了?看起来是不是也没有重复做功?

但是仔细的思考看看就会发现,我们人在找这个切断的时候,是通过眼睛完成的!但是如果机器需要通过遍历才能找到的话,还是需要在里面再重复一遍,因此复杂度不一定可以降下来。[苦涩]

这个时候我们灵机一动,想起来了在leetcode两数相加中学习到的一个数据结构,哈希表!哈希表同学可以帮助我们在一堆数中,用O(1)的复杂度就找到对应的数字,这个和人的定睛一看,异曲同工之妙啊。剩下的问题就是,怎么定位到切除点了。我们可以发现切除点一定是顺着进行的,因此可以设置一个在前面的指针,记录上一个切除点的位置,当遇到和后面进行的指针

怎么写代码?怎么到下一个?

需要在中间处理用的过程的数据和处理的步骤都思考好了,开始写代码!猛然发现,还有一个问题没有处理就是,怎么让一切进行啊~简单的思考是,用while和for都可以。但是从思考来说,其实for可能更简单一些,下面来看看用for和while的时候,脑子思考的都是什么东西,写出来的代码是什么样子的。脑子思考的东西我放在代码注释里面,解释的超级清楚!

while

用while的时候其实需要思考的东西相对的多些,需要想清楚进入的判断条件是什么,需要思考清楚循环内会怎么修改这个数字,以及是不是能够没有问题的退出循环。在循环退出之后,思考退出之后得到的数字都是怎么样子的,还需要怎么处理以及返回值是什么样子的。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) < 2: return len(s)
        record = set()
        left, right = 0 , 0  # 左右边界,左闭右闭
        record.add(s[left])  
        maxLength = 1  # 目前的集合的长度
        while right <= len(s) - 2:  # 当集合的右边还是有数的时候,即可以继续往后一个
            if s[right+1] not in record:  # 可以继续往右边挪的情况
                record.add(s[right+1])
                right += 1
                continue
            maxLength = max(maxLength,len(record)) # 挪不了了找到了一个子串
            while s[right+1] in record:  # 寻找截断点
                record.remove(s[left])
                left += 1  # 表示集合的最前面数字的index
        return max(maxLength,len(record))

这里我们进入循环的条件是,右边没有到最后(因此前面写了长度小于2的时候,是不满足这个情况的),并且在循环内每次最多只会对于右边挪动一个位置。因此最终结束的时候,右边一定到了最后的,但是左边是不能确定的,record里面都可能是有数的,并且这个时候的长度是没有和maxLength进行过比较的,因此返回两者中的较大值即可。

为什么这样的算法可以时间复杂度是O(n)呢?因为我们发现我们挪动的只有左右,且不会往回移动,因此最多就是全部走一遍,就是O(n)(因为中间的判断需不需要截断的部分,我们用哈希表做到了时间复杂度为O(1))。

for

同时,我们也发现,整个过程中,挪动的结束的时候,右边一定是在最后的,因此也可以拿这个来控制。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) < 2: return len(s)
        left = 0
        record = {s[left]}
        maxLength = 1
        for i in range(1,len(s)): # 控制右边界的右边元素
            if s[i] not in record:
                record.add(s[i])
                continue
            maxLength = max(maxLength,len(record))
            while s[i] in record:
                record.remove(s[left])
                left += 1
            if s[i] not in record: record.add(s[i])
        return max(maxLength,len(record))

这个地方需要注意的是,每个循环内,右边一定都会往右边挪一个。因此,每个循环内需要完成的所有工作,包括把挪之前的右边加进去,也需要考虑到,要在这个循环内完成。

也可以考虑控制左边来写,写出来的代码如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s) <2 :return len(s)
        maxLength, right = 0,0
        record = set()
        for i in range(len(s)):
            if i != 0:
                record.remove(s[i-1])
            while (right<len(s)) and (s[right] not in record):
                record.add(s[right])
                right += 1
            maxLength = max(maxLength,len(record))
        return maxLength

即在每个循环内都需要找到这个字母开头的时候最大的子串,再全部进行比较。这个和我们的截断的思想不是很一致,这个的思想是,按照开头字母来看,这个字母开头的时候,最大的是多少。

思路总结

主要有两种思路:
(1)图中的截断的思想,碰到在集合中的情况的时候,去寻找前面应该截断的点。
(2)开头思想,每个开头都对应了一个最大的子串,因此把每个开头的情况比较一下,得到最大值。

while和for循环总结

在写for和while的时候,需要考虑的东西是不同的。while考虑的是进入的条件,方法体中对此条件的更改,以及这一次循环得到的最后的状态是什么样子的。

写for的时候,直接规定了这次循环的存在,以及这次循环内需要做的事情。直接控制了某些变量最终的状态。比while更强一些。

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

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

相关文章

一台电脑上安装多个版本的python,运行互不干扰,显示位置的一些命令,

首先需要知道一些命令&#xff1a; pip show 包名 可以使用pip show 包名的方式显示位置 pip list pip方式显示的是当前环境下的库 os.environ.get&#xff08;&#xff09; python中os模块获取环境变量的一个方法 Python os.environ.get&#xff08;&#xff09;的用法 …

使用Git LFS上传Unity大型资源文件

在使用Unity制作结课作业时&#xff0c;使用到git工具进行版本控制与多人协作。在提交项目至远程仓库的过程中&#xff0c;git bash提示了以下报错&#xff1a; remote: warning: File Assets/Models/Z_India_5.29.fbx is 57.31 MB; this is larger than GitHubs recommended m…

利用环回口建立IBGP邻居

利用环回口建立IBGP邻居 BGP的稳定性 IBGP : 1.一般使用环回口建立IBGP邻居 2.指定建立IBGP邻居的源地址为lookback地址 EBGP : 1.一般使用直连接口建立EBGP邻居关系 2.如果想使用环回口建立EBGP邻居&#xff0c;需要将TTL值修改为大于1&#xff0c;默认TTL1 修改命令…

在win10/11的右键菜单添加电源选项

前言&#xff1a; 今天&#xff0c;博主从网上找到了在右键菜单中添加电源选项的方法&#xff0c;觉得挺实用的所以来教大家 方法&#xff1a; 下载&#xff08;懒人专用&#xff0c;直接打开文件即可&#xff09;&#xff1a; csdn中下载&#xff08;启用和关闭文件都有&a…

基于 Docker_redis6.0.8 实现 Redis 集群扩缩容

文章目录 单机部署数据分区方案集群部署集群容错测试集群扩容测试集群缩容测试 LXL&#xff1a;这玩意太枯燥&#xff0c;看完需要耐心 ~~~ 单机部署 通过 dockerhub 查找 redis 镜像&#xff0c;选择 6.0.8 版本。创建挂载目录&#xff0c;并赋予权限。 mkdir -p /var/docker…

MicroPython应用基础-使用Thonny IDE

MicroPython应用基础-使用Thonny IDE 文章目录 MicroPython应用基础-使用Thonny IDE引言Thonny简介使用Thonny连接到MicroPython开发板使用Thonny的REPL窗口运行Python语句在Thonny中保存Python程序文件至MicroPython开发板中运行使用Thonny的注意要点 引言 在很长一段时间内&…

[论文分享]TimeMAE:解耦掩码自编码器时间序列的自监督表示

论文题目&#xff1a;TimeMAE: Self-Supervised Representations of Time Series with Decoupled Masked Autoencoders 论文地址&#xff1a;https://arxiv.org/abs/2303.00320 代码地址&#xff1a;https://github.com/Mingyue-Cheng/TimeMAE 1 摘要 利用自监督预训练增强基于…

Flume和Kafka的组合使用

一.安装Kafka 1.1下载安装包 通过百度网盘分享的文件&#xff1a;复制链接打开「百度网盘APP 即可获取」 链接&#xff1a;https://pan.baidu.com/s/1vC6Di3Pml6k1KMbnK0OE1Q?pwdhuan 提取码&#xff1a;huan 也可以访问官网&#xff0c;下载kafka2.4.0的安装文件 1.2解…

Hadoop3.1.3安装(单机、伪分布)

系列文章目录 Ubuntu常见基本问题 Hadoop3.1.3安装&#xff08;单机、伪分布&#xff09; Hadoop集群搭建 文章目录 系列文章目录一、环境1、创建hadoop用户 二、更新apt三、安装SSH、配置SSH无密码登陆四、安装Java环境五、安装 Hadoop3.1.3六、Hadoop单机配置(非分布式)七、…

chatgpt赋能python:Python为什么被吹得这么神?

Python为什么被吹得这么神&#xff1f; Python是一个开源、跨平台的高级编程语言&#xff0c;由Guido van Rossum于1989年在荷兰创造。Python在近几年因其方便易用、高效稳定和丰富的生态体系而备受欢迎。Python被广泛应用于数据科学、人工智能、机器学习、Web开发、自动化测试…

启动虚拟机并安装Linux系统

我们刚刚新建的虚拟机相当于一个裸机&#xff0c;还没有安装操作系统在里面&#xff0c;下面我们来看一下怎么进行Linux系统的安装。 在VMware Workstation工具的主界面选择虚拟机Spark01&#xff0c;单击鼠标右键在弹出的菜单中选择“设置”打开“虚拟机设置”对话框。如下图…

程序设计综合实习(C语言):学生成绩文件管理

一、目的 1&#xff0e;掌握文件指针的概念和运用 2&#xff0e;掌握文件的相关操作&#xff1a;打开、读、写、关闭 3&#xff0e;掌握文件的定位操作 二、实习环境 Visual Stdio 2022 三、实习内容与步骤 1&#xff0e;定义一个结构体数组&#xff0c;存放10个学生的学号&a…

ABB Drive Composer Pro 2.8.1 Crack

Drive Composer 是 ABB 通用架构驱动器的启动和维护工具。该工具用于查看和设置驱动器参数&#xff0c;以及监控和调整过程性能。 Drive Composer入门版提供了设置参数、基本监控、从 PC 对驱动器进行本地控制以及事件记录器处理等基本功能。 Drive Composer pro是成熟的调试和…

蓝桥杯ABC组 数论知识合集

Note Of Note 同余方程中是可以正常进行分数的约分和去分母的 e x g c d exgcd exgcd 在使用时要保证 a , b a,b a,b 都是非负数 a a a 与 b b b 互质不代表 a , b a,b a,b 都为质数&#xff08; 4 4 4 和 5 5 5 互质&#xff0c;但是 4 4 4 不是质数&#xff09;两个相邻…

第3章“程序的机器级表示”:控制

文章目录 3.6 控制3.6.1 条件码3.6.2 访问条件码3.6.3 跳转指令及其编码3.6.4 翻译条件分支3.6.5 循环do-while 循环while循环for循环 3.6.6 switch 语句 3.6 控制 截止目前&#xff0c;考虑了 访问数据和 操作数据 的方法。程序执行的另一个很重要的部分就是控制被执行操作的…

Maven高级5-私服

1. 简介 一台独立的服务器&#xff0c;用于解决团队内部的资源共享与资源同步问题&#xff08;模拟了中央服务器&#xff09;&#xff1b; https://help.sonatype.com/repomanager3/download 命令行启动服务器 在nexus.exe目录&#xff08;bin目录&#xff09;下启动cmd&#…

【认知提升思维篇】之 反刍思维--恶性思考的根源

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;普本…

代码随想录算法训练营第五十一天 | 力扣 309.最佳买卖股票时机含冷冻期, 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 题目 309. 最佳买卖股票时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一…

Vulnhub | 实战靶场渗透测试 - PRIME: 1

0x00 免责声明 本文仅限于学习讨论与技术知识的分享&#xff0c;不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;本文作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担…

“本草”大模型开源,ChatGPT时代,连AI私人医生都出现了?

大家好,我是千与千寻,也可以叫我千寻,今天给大家分享的ChatGPT新应用项目,是ChatGPT模型在医学领域的应用,什么,医学领域? 是的,没错,是医学领域的ChatGPT应用,我们都知道ChatGPT是OpenAI开源的一个智能对话式引擎,今天给大家分享的项目叫“本草”。 “本草”模型是…