刷题网站:Leetcode
难度: 中等
语言: Python
计划:从简单——>到中等——>再到难。
一、3无重复字符的最长字串
1.1 问题描述
给定一个字符串s
,请你找出其中不含有重复字符串的最长子串的长度。(这里要与最长子序列做区分,最长子串要连续的字符)
- 示例1
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
- 示例2
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
- 示例3
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
- 示例4
输入: s = ""
输出: 0
1.2 思考分析
最初,我的思考是这样的,既然题目要求不重复的最长子串,那我可以以第一个为起点,与后面的一个一个比,然后以第二个为起点,一个一个与后面比,直到比到重复的记录下,然后往后移动。初步代码如下
for left in range(len(s)): #假设我只从第一个字符开始为左指针
right = left + 1 #比左指针加1为右指针开始比
while s[left] != s[right]: #只要左指针不等于右指针的值,就右指针往后移
right += 1
print(right - left) #输出最长的子串
从代码上理解我刚开始还觉得行,但是运行后发现会发生溢出(出错),上面的代码仅仅考虑了起始位置固定,与后面字符进行比较,并没有考虑在左指针和右指针内字符的比较,所以是错的。例如,当left = 3
时为a
,right = 4
时为b
然后,right
一直增加到最后都没有与a
重复的,看似是满足上面代码abcbb
,但是,里面还有b
是重复的,所以错了。
所以还需考虑左指针右指针内部的比较,以及右指针溢出的可能,所以代码改如下:
for left in range(len(s)):
right =left +1
if left == len(s)-1:
break
else:
while s[left] != s[right]:
right +=1
if s[left+1] == s[right]:
break
print(right-left)
但是,我们还需要返回不重复子串的最大数呀,所以想到定义一个列表,将right-left
的值逐个存入,然后输出最大的数,即
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if s != "":
list = [1]*len(s)
for left in range(len(s)):
right =left +1
if left == len(s)-1:
break
else:
while right != len(s) and s[left] != s[right]:
right +=1
if right == len(s) or s[left+1] == s[right]:
break
list[left] = right-left
return max(list)
else:
return 0
看似把第一个例子带入后,输出的结果也是3
,带入第二个例子后也是1
,第三个例子输出也是3
,带入最后的空,输出也是0
,感觉是对的。其实此答案还是错误的,我只是为了凑出结果而编的代码,还是看官方解答吧!!!😂
看了官方解答后的一些思考。
此题提到了无重复字符
,则最先考虑用滑动窗口来解答。所以,我们需要知道滑动窗口的含义,举个例子。当考虑字符串为abcabcbb
时,我们从左到右,逐个开始为起始位置,来判断最长的子字符串大小(想法与我的类似?)。即
- 第一个字符为起点时,
(a)bcabcbb
,则最长走到(abc)abcbb
,若再往下“滑动”的话,就会变成(abca)bcbb
,有重复a
不行。所以最长为3. - 第二个字符为起点时,
a(b)cabcbb
,则最长走到a(bca)bcbb
,若再往下“滑动”会有重复,所以最长为3. - 第三个字符为起点时,
ab(c)abcbb
,则最长走到ab(cab)cbb
,最长为3. - 第四个字符为起点时,
abc(a)bcbb
,则最长走到abc(abc)bb
,最长为3. - 第五个字符为起点时,
abca(b)cbb
,则最长走到abca(bc)bb
,最长为2. - 第六个字符为起点时,
abcab(c)bb
,则最长走到abcab(cb)b
,最长为2. - 第七个字符为起点时,
abcabc(b)b
,则最长走到abcabc(b)b
,最长为1. - 第八个字符为起点时,
abcabcb(b)
,则最长走到abcabcb(b)
,最长为1.
首先注意的点是,起始位置增加后,子串的结束位置也是递增的。我发现,我与原答案的区别在于判断左右指针内的重复字符,官方解释可以用python中的set
,即左指针向右移动时,从set
中移除一个字符,在右指针向右移动时,往set
中添加一个字符。(其中右指针初始值为-1,相当于在左边界,还没开始移动)
修改后的代码如下:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
list = set()
Max = 0
right = -1
for left in range(len(s)):
if left != 0:
list.remove(s[left-1])
while right +1 < len(s) and s[right + 1] not in list:
list.add(s[right+1])
right+=1
Max = max(Max, right - left +1)
return Max
运行结果显示,击败了39.28%的用户,看来还是不是算好的答案
1.3 总结
这是第一次解处滑动窗口题目,还是有些地方没注意到,花了挺久时间想的,看官方答案也没完全吸收,后续还要多加练习才行。特别是要注意移动后,增添、删减操作。