2024.3.26
Leecode3——无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
个人解答
先上最简单的思路,序列其实就是起点和终点。用快慢指针,慢指针遍历整个字符串,作为无重复字符串的起点,快指针在慢指针右侧搜索,没见过的字符就加入max_length_list,遇到有重复的就要开始计算长度了,最后修改最大长度的值即可。
遇到个小坑,要考虑快指针把字符串遍历完的情况。代码如下:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
length = len(s)
if length == 1:
return 1
max_length = 0
for i in range(0, length):
max_length_list = []
for j in range(i, length):
if s[j] not in max_length_list:
max_length_list.append(s[j])
if j == length-1:
max_length = max(max_length, len(max_length_list))
else:
max_length = max(max_length, len(max_length_list))
break
return max_length
不过两个循环,耗时还是很慢的:
耗时:1371ms(5.02%)
内存:16.4MB(88.21%)
优化,启动!
1.滑动窗口
优化上面这个代码,最简单的思路就是使用滑动窗口,类似于一个队列(先进先出)。
那么大致逻辑上就只需要一次循环:
构建当前的无重复的字串序列current_list,遍历s中的字符,如果没有出现过,就加入;如果出现过,那么就一直删去current_list的首元素,直到删到当前字符没有出现过,继续循环。
代码如下:
class Solution(object):
def lengthOfLongestSubstring(self, s):
length = len(s)
max_length = 0
current_list = []
for i in s:
if i not in current_list:
current_list.append(i)
else:
while(i in current_list):
current_list.remove(current_list[0]) # 队首元素出队
current_list.append(i)
max_length = max(max_length, len(current_list))
return max_length
耗时:83ms (19.82%)
内存:16.44MB (80.04%)
2.Python中的集合set和列表list
(1)set和list在形式上的区别,set中不允许出现重复的元素,而list可以;
(2)最关键的一点:查找效率不同!!!
单次查询,list的时间复杂度为O(n),需要遍历整个list;而由于set进行了去重,使用了红黑树这种高效的查询技巧,时间复杂度为O(logn)(理想情况下为O(1))。
所以,在涉及大规模查找时,效率:set>dict>list(list中,[]>list())
(3)set是无序的,和list不同,因此set不能用来索引和切片。
一个用set写的代码参考如下:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
left = 0
lookup = set()
n = len(s)
max_len = 0
cur_len = 0
for i in range(n):
cur_len += 1
while s[i] in lookup:
lookup.remove(s[left])
left += 1
cur_len -= 1
if cur_len > max_len:
max_len = cur_len
lookup.add(s[i])
return max_len
tips:
1.set常用的操作:增加元素(add(x)),删除元素(remove(x)), x in set,len()等。
2.由于无法使用切片,所以上面这段代码里面,使用了left来记录要删除的起点位置,lookup里删除的不是lookup里重复的地方,而是s中left对应的元素。这一点还是很巧妙的!
耗时:51ms (88.89%)
内存:16.54MB (41.24%)