【手绘算法】力扣 3 无重复的最长字符串(Longest Substring Without Repeating Characters)

简介: Hi,大家好,我是Tom。一个美术生转到Java开发的程序员。今天给大家分享的是力扣题第3题,无重复的最长字符串。在解题过程中,也会手写一些伪代码。当然,如果想要完整的源码的话,可以到我的个人主页简介中获取。这道题呢属于中等难度,评估为四颗星,它的通过率只有39%。

Hi,大家好,我是Tom。一个美术生转到Java开发的程序员。今天给大家分享的是力扣题第3题,无重复的最长字符串。在解题过程中,也会手写一些伪代码。当然,如果想要完整的源码的话,可以到我的个人主页简介中获取。

这道题呢属于中等难度,评估为四颗星,它的通过率只有39%。

5b3bde6e90ed4e83a1030d69fbcd7ceb.png

1、题干分析

首先,我们还是来分析一下这道题的题干。它说的是,给定一个字符串 s,让我们来找出不含重复字符的 最长子串 的长度。注意,这个地方不重复的最长子串是指它其中连续的字符串的部分。比如说,官方给了几个示例,我看第1个示例:

d42f236b78df4c4cab1fa3d8eedeebf1.png

对于这道题,我给出两种解题方法。第一种方法是暴力法求解,第二种方法是滑动窗口求解。

a346975f9d1649c7a1d047c9fabd8fa0.png

2、暴力法求解

首先,我们来分析一下暴力法求解的思路。这个答案我们目测,最长不重复子串为 bacd ,它的长度为4。但是程序怎么才能判断出来呢?其实暴力法的逻辑分厂简单,就是字符串中的每个字符和后面的进行比较,一直累计不重复的次数,最后,将累计最大的值返回。

首先,我们定义一个方法叫 checkUnique(),专门用来检测是否重复,如果重复就返回 fasle ,如果不重复就返回 true。

然后,我们用两个循环,依次比较,比如第一层循环游标为 i,第二层循环游标为 j。

第一轮比较的游标值为 i = 0,j = 1。首先拿 a 和 b 比较,发现不重复就累加,然后,a 和 c 比较发现不重复,继续累加。然后,a 和 b比较依次类推。

第一轮比较完成之后,进行第二轮比较,游标值 i = 1,j = 2, 也就是拿 b 和 c 比较,不重复累加,b 和 b 比较重复 不累加,然后b 和 a比较以此类推。

最后,依次比到第九轮,i = 8,j = 9,我们最终得到累计的最大值。

976bd202dfe04a5f81442e443940b8f6.png

这是暴力法求解的伪代码:

5f6e9af90604439f860ca6c9f95ee2da.png

当然,暴力求解法是最笨的方法,也是效率最低的办法,我不推荐使用。

3、滑动窗口求解

下面,给大家介绍一种更高效的解法,滑动窗口求解,也叫双指针求解,我们先来理解一下滑动窗口的解题思路。我们定义一个左指针 left 和右指针 right 进行实时地滑动,一般情况下这两个指针都是往右滑动。右指针最先开始滑动,只要满足条件就会一直往右滑动,当不满足条件的时候,左指针才开始往右滑动,直到能让右指针满足条件为止。那么,左指针和右指针之间的区间,我们就可以理解为滑动的窗口。当然,这个滑动的窗口可以是固定宽度,也可以是可变宽度。

b7072d283eab4055bf08bd95478694de.png假设有这样一个字符串 “ abcbacdca ”,下面我们利用滑动窗口的思想来快速找到这个字符串中不重复的最长子串。最开始的时候,我们把左指针和右指针同时指向这个字符串的首位置。同时,我们还定义一个 length 来记录当前遍历到的当前窗口的长度,默认赋值为字符串的长度为8。然后,定义 maxLength ,表示当前不重复子字符串的最大长度用于结果输出,默认为 0。然后,还要定义一个数组 window,用于标记窗口中的元素。这个数组的长度我固定为128,因为,一会我将要利用字符的 ASCII 码作为数组的索引下标,然后,在数组对应的下标位置坐标记。而 ASCII 码的 范围 0- 127,也就是128个值,正好覆盖到所有的字符。

792d4557ab1b41b39892b8db679f7182.png

首先是右指针就要开始往后移动。右指针每遍历到一个元素呢,它都要放入到 window 中,如果发现 window 中已经存在遍历过的元素,说明,左指针就要往后移动。

接下来,我们来模拟程序的执行逻辑。首先,左指针 和 右指针默认都指向 a 这个字符。

第一步,将右指针指向的 a 字符放入到 window 中,而 a 的 ASCII 码是 97,所以在下标为 97 的位置标记为 1,表示 a 出现的位置为 1。maxLength 加 1更新为1,同时,右指针往后移动。

第二步,将右指针指向的 b 字符放到入到 window 中,下标为 98 的位置,并且标记为 2,表示 b 出现的位置为 2。maxLength 加 1更新为2,同时,右指针继续往后移动。

第三步,将右指针指向的 c 字符放入到 window 中,下标为 99 的位置,并且标记为 3,表示 c 出现的位置为 3。maxLength 加 1更新为3,同时,右指针继续往后移动。

第四步,将右指针指向的 b 字符放入到 window 中,下标为 98 的位置,这个时候,发现 b 在前面出现过,所以,左指针要移动到 b 上一次出现的位置之后,也就是 c 的位置下标为 2。然后,再将下标为 98 的值更新为 b 出现的位置为 4,同时,右指针继续往后移动。

第五步,将右指针指向的 a 字符放入到window中,下标为 97 的位置,这个时候,我们又发现 a 在之前出现过,所以,左指针又要移动到 a 上次出现的位置之后,也就是 b 的位置下标为 1。然后,再将下标为 97 的值更新为 a 出现的位置为 5,同时,右指针继续往后移动。

第六步,将右指针指向的 c 字符放入到window中,下标为 99 的位置,这个时候,我们又发现 c 在之前出现过,所以,左指针又要移到 c 上次出现的位置之后,也就是 b 的位置,下标为 3。然后,再将下标为 99 的值更新为 c 出现的位置为 6,同时,右指针继续往后移动。

第七步,将右指针指向的 d 字符放入到 window中,下标为 100 的位置,d 在之前没有重复出现,所以将 99 的值标记为 d 出现的位置为7,左指针保持不动,maxLength 加 1 更新为 4,右指针继续往后移动。

第八步,将右指针指向的 c 字符放入到window中,下标为 99 的位置,这时我们又发现 c 在之前出现过,所以,左指针又要移到 c 上次出现的位置之后,就是 d 的位置,下标为 6。然后,再将下标为99的值更新为 c 出现的位置为 8,同时,右指针继续往后移动。

第九步,将右指针指向的 a 字符放入到windown中,下标为 97 的位置,这时我们发现 a 又在之前出现过,所以,左指针又要移动 a 上次出现的位置之后,也就是 c 的位置,下标为 5,然后,再将下标为 97 的值更新为 a出现的位置为9,同时,右指针继续往后移动。

这时候,右指针已经超出了字符串的范围,所以,最终maxLength的值顶格在 4。

5150013eea9c47c6909b92de6cfc4970.png

这是滑动窗口求解的伪代码:

feaf55ab783347e39a4729e9d97455a1.png

以上就是我对于无重复的最长字符串这道题的解题思路分享。只要是遇到字符串查找相关的问题,利用双指针有非常好的适用性。这道题除了滑动窗口求解以外,其实还可以使用动态规划等方法求解。如果需要完整代码的小伙伴,可以在我的个人主页简介中获取。

0b055dbf77b44ec7ac5125e3ecda03d2.png

我是被编程耽误的文艺Tom,如果我的分享对你有帮助,请动动手指一键三连分享给更多的人。关注我,面试不再难!

相关文章
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
9天前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
1月前
|
算法
两个字符串匹配出最长公共子序列算法
本文介绍了最长公共子序列(LCS)问题的算法实现,通过动态规划方法求解两个字符串的最长公共子序列,并提供了具体的编程实现细节和示例。
73 1
两个字符串匹配出最长公共子序列算法
|
24天前
|
JavaScript
力扣3333.找到初始输入字符串Ⅱ
【10月更文挑战第9天】力扣3333.找到初始输入字符串Ⅱ
31 1
|
1月前
|
C++
Leetcode第43题(字符串相乘)
本篇介绍了一种用C++实现的字符串表示的非负整数相乘的方法,通过逆向编号字符串,将乘法运算转化为二维数组的累加过程,最后处理进位并转换为字符串结果,解决了两个大数相乘的问题。
24 9
|
1月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
65 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
1月前
|
算法
【链表】算法题(二) ----- 力扣/牛客
【链表】算法题(二) ----- 力扣/牛客
|
1月前
|
算法 C++
Leetcode第八题(字符串转换整数(atoi))
这篇文章介绍了LeetCode上第8题“字符串转换整数(atoi)”的解题思路和C++的实现方法,包括处理前导空格、正负号、连续数字字符以及整数溢出的情况。
17 0
|
1月前
【LeetCode 22】459.重复的子字符串
【LeetCode 22】459.重复的子字符串
28 0
|
1月前
【LeetCode 20】151.反转字符串里的单词
【LeetCode 20】151.反转字符串里的单词
19 0