题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s = “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s = “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示例 3: 输入: s = “pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。 请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。 示例 4: 输入: s = “” 输出: 0 提示: 0 <= s.length <= 5 * 104 s 由英文字母、数字、符号和空格组成 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters 著作权归领扣网络所有。商业转载请联系官授权,非商业转载请注明出处。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
做题经历
这个文章当中,我会比较详细的以我自身的做整道题的过程进行讲解,可能有些错误或是没有注意到的地方可能看起来有点蠢,请大佬勿喷,毕竟这是我第一次在力扣上做题。另外就是说,可能也有其它像我一样的小白,在做这道题的时候看一遍别人的题解,也明白,然后自己上手的时候,就会报这样那样的错误。所以这算是记录自己的做题经历,也是提供给其它和我一样的小白一些借鉴。 (如果感觉不需要过分看我的做题情况的话,可以直接跳到最后的总结)
分析
在这次练习中,从一直报错、更改、到运行成功,大概改了四次(可能更多,但我在本地编辑器里记录了四次),然后运行成功后,又进行了两次优化。先张贴一下我运行的情况 最后还有一部分是简化代码的部分我就不张贴了。 最好情况是32ms,内存占用14.8(当然这俩不是同时出现的) 还要特别说明一下,我的全程是没有看过别人的题解的,所以绝对不是拿别人的东西写给大家!!!
开始分析
首先拿到这道题的时候,扫描一个最长字串,后来看题解的时候知道有一个滑动窗口的概念,力扣官网的网址,已经放在这了,有兴趣的可以自己看一下,在这我就不去讲这个了。还有就是我也不会去介绍所有情况吧,类似于用两重循环暴力求解这种,本身我们来刷题是提高自己算法优化的能力,那么就不适合在这写出这种东西。
回归正题,扫描一个最长字串,我的一个想法是,创建一个队列,遍历这个字符串,每一次遍历都往队列当中添加遍历的字符,当队列中存在相同的字符时,开始出队,直至队列里面没有相同的元素。然后用一个变量来记录队列最长时的长度。这个想法和滑动窗口意思差不多,只是我当时不知道,当家可以看一下视频,有动图展示会更生动,好理解。
第一次代码
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
string = ''+s[0]
length = []
for i in range(1,len(s)):
if s[i] in string:
string = string[1:]
if string[-1] == s[i]:
length.append(len(string))
string = ''
string = string + s[i]
length.append(len(string))
return max(length)
a = Solution()
print(a.lengthOfLongestSubstring('abcabcb'))
>>> 3
print(a.lengthOfLongestSubstring('bbbbb'))
>>> indexerror:string index of range line 9
很长而且很垃圾,但是我还是要说,嘿嘿,这段代码运行是没有错的,但是当把’bbbbbbb‘作为参数运行的时候会报一个indexerror的错误。虽然代码很垃圾,我现在看也是这么觉得的,但是当时运行过第一个案例我感觉贼厉害。我就先说一下我这段代码的思维,以及报错原因把。毕竟我敲字的时候也看不下去。
基本上我都注释在上边每一步是做什么了,但简单讲一下代码部分 为什么在string后边要加一个s[0]?
因为我是在循环的最后进行一个入队的操作,但是在第二个if那里有一个string[-1],如果string是空字符串的话,会报一个indexerror的错误
为什么’bbbbbb’作为参数时会报错?
可以看到第一个分支那里,因为都是’b’所以string删除了首个字符成了空字符串,所以会报错
第二次代码
对上次的修改,我发现其实第二个分支其实是属于第一次分支内所包含的,所以进行了如下调整
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
string = ''+s[0]
length = [1]
for i in range(1,len(s)):
if s[i] in string:
if string[-1] == s[i]:
print(string)
string = s[i]
continue
string = string[1:]
string = string + s[i]
length.append(len(string))
print(string)
return max(length)
a = Solution()
print(a.lengthOfLongestSubstring('ckilbkd'))
> ck
> cki
> ckil
> ckilb
> kilbk
> kilbkd
> 6
此次调整后’bbbbb‘的结果运行成功,但是在该参数中,结果输出错误,很明显结果应该是5,但是输出了6,然后我用print打印了一下过程,可以发现在第二个k出现的时候,程序只做了一次出队的操作,但是呢,第一个k是的位置在1(设队首为0),所以在后续的length中添加的情况实际是有两个k的,那么造成结果错误。
第三次代码
这次代码我就只针对这次情况投机取巧了一下,用了一下集合的性质,但不建议大家像我这么做,还是踏踏实实做。但是当输入下一个测试案例的时候我发现我的全盘都需要改
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
string = ''+s[0]
length = [1]
for i in range(1,len(s)):
if s[i] in string:
if string[-1] == s[i]:
print(string)
string = s[i]
continue
string = string[1:]
string = string + s[i]
length.append(len(set(string)))
print(string)
return max(length)
a = Solution()
print(a.lengthOfLongestSubstring(“”))
> indexerror
输入一个空字符串,很显然在声明string和赋值的时候会报这个错误,因为我后来的分支有必须要那么去定义这个变量,就说明我的这个思路有问题的,但是我还是挣扎了一下,我把空字符串当成一种特殊情况,直接返回0,但是也对其它的部分进行一点点修改,而且为了节省空间改用一个变量储存长度。而且也去掉了那个集合的投机行为,以为之后我也有因为没有处理好那个而报错
第四次代码
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if s == "":
return 0
string =""+s[0]
temp = len(string)
for i in range(1,len(s)):
while s[i] in string and string != '':
if string[-1] == s[i]:
string = string.lstrip(string[0])
break
string = string.lstrip(string[0])
string = string + s[i]
if len(string)>temp:
temp = len(string)
return temp
a = Solution()
print(a.lengthOfLongestSubstring('pwwkew'))
> 4
结果应该是3但是这里输出了4。关键点在于while循环里的分支终止。因为这个分支是对两个相同字符挨在一起的情况进行处理,那么也就是应该,删除掉之前所有的元素,但是这里的break就只是删除了一次。所以说应该把break改为continue,那么改为continue之后,就运行成功了
运行成功
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if s == "":
return 0
string =""+s[0]
temp = len(string)
for i in range(1,len(s)):
while s[i] in string and string != '':
if string[-1] == s[i]:
string = string.lstrip(string[0])
continue
string = string.lstrip(string[0])
string = string + s[i]
if len(string)>temp:
temp = len(string)
return temp
左边的都是这段代码的运行情况,我们就运行成功了!!![撒花] [撒花]
简化代码
这段代码虽然运行成功了,但是我感觉可读性和简洁程度都有很大的改善,而且有些多余的条件判断可以去掉来提高性能,我就直接贴代码了。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
string =""
temp = len(string)
for i in s:
while i in string and string != '':
string = string.lstrip(string[0])
string = string + i
if len(string)>temp:
temp = len(string)
return temp
那么这段代码就相比较之前那一个更加pythonic,思路的话就是我最开始所讲的思路,也都呈现在了这里,然后在写这篇文章时,又运行了一次给大家看一下效果。然后如果有大佬认为还可以继续改进优化,可以私信我或评论。
总结
简单总结一下,我在这道题中的体会。
第一个:关于题目 其实大家要是像我一样每一次这种思路的话,其实可以发现,每一次我们所遇见的都是一个新的问题,而我只是针对每一次问题进行了解答。但是我们需要做的是一个通解。 比如’bbbbb‘ 是要求每一次队列里只有一个 ’ckilbkd‘,是要求你不能出一个进一个 ’‘空字符,也是检验你的程序情况等 其实每一个测试都是反映了一种特殊情况,只不过我这是隔天写的,也就忘了当天的一些总结。如果有同学和我一样幸运,几乎每一个新的测试案例都会报错,那么你应该也会知道每个点都在哪。 但是最后思想都是我最开始的那个思想,就是利用队列,或者说是滑动窗口,可能官方还有更好的题解,但那不是我的,所以我没有列出来
第二个:关于代码 大家可以发现,我最开始的代码看起来可读性很差,以及逻辑思路不清晰,甚至对于一些基本操作用了很奇怪的方法,这就是emmm…,没有一个好的编程习惯,我指的是经常敲,那么就让我的一些这种能力很差,以至于写出来的代码也很让人头疼。甚至说我在里边因为lstrip()函数的使用,都用了很长时间,才发现,她并不会去修改原字符串,需要重新赋值,所以我最开始用的切片操作。所以还是经常敲一敲代码,养成一个好的习惯。
再BB两句
这是我第一次刷力扣的题,其实曾经也尝试过,因为不是计算机专业,没有学过数据结构所以很吃力,甚至说都看不懂别人的题解,然后现在因为是开学大三,有准备跨考计算机的打算,所以说呢暑期看了看数据结构(写这篇文章的时候还没看完,刚到二叉树),那么也想尝试一下,所以我能做出来还是很有成就感的,而且一次次优化,看着运行时间一次次减少,真的很兴奋。现在也是有这么一个打算,会定期的开始更新一些力扣题库的做题记录,算是在这打卡把。我也不知道我能坚持多久,因为好像写这个文章好像也很费时间,我也希望大家可以私信我或者多多评论交流,让我保持着这么一份热情,能够一直写下去。同时也希望大家可以对我的错误指正或者拓展一些知识,万分感谢~~~
谢谢大家~
|