3. 无重复字符的最长子串
- 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
思路1:切片实现滑动窗口(时间复杂度 O ( n 2 ) O(n^2) O(n2))
定义一个滑动窗口列表,遍历字符串:
若字符不在窗口中,则添加,扩展窗口;
若字符已经在窗口中,则将窗口先缩减到窗口列表中字符出现位置的下一字符处,再添加当前字符,保证窗口列表中无重复字符。
代码实现1:
class Solution(object):
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
window = [] # 滑动窗口列表
max_length = 0 # 最长串长度
for c in s:
# 如果字符不在滑动窗口中,则直接扩展窗口
if c not in window:
window.append(c)
# 如果字符在滑动窗口中,则
# 1. 从窗口中移除重复字符及之前的字符串部分,新字符串即为无重复字符的字符串
# 2. 再扩展窗口
else:
window = window[window.index(c) + 1:]
window.append(c)
# 更新最大长度
max_length = max(len(window), max_length)
return max_length
思路2:使用set()(时间复杂度 O ( n ) O(n) O(n))
定义一个哈希set,一个快指针,一个满慢针
遍历字符串:
- 如果遇到已经存在于set()中的值,说明 s[j] in hash_set,将慢指针i一边向前移动,一边删除对应元素,直至set()中没有当前遍历的值
- 快指针向前走,如果遇到新值,就向set()中添加
- 每次遍历过后都判断一下set的长度是否为最大长度
代码实现:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
hash_set = set()
max_len = 0
i = 0
for j in range(len(s)):
while s[j] in hash_set:
hash_set.remove(s[i])
i += 1
hash_set.add(s[j])
max_len = max(len(hash_set), max_len)
return max_len
思路3:使用哈希表优化(时间复杂度 O ( n ) O(n) O(n))
定义一个哈希表,一个快指针i,一个满慢针left
遍历字符串:
- 如果遇到已经存在于哈希表中的值,就取当前left和哈希表中已经出现值的索引+1最大的那个
- 快指针向前走,如果遇到新值,就向哈希表中添加
- 每次遍历过后都判断一下 快指针 - 慢指针 + 1 之间的长度是否为最大长度
代码实现:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
hash_map = {}
max_len = 0
left = 0
for i, c in enumerate(s):
if c in hash_map:
left = max(left, hash_map[c] + 1)
hash_map[c] = i
max_len = max(i - left + 1, max_len)
return max_len
思路4:使用哈希表优化滑动窗口(时间复杂度 O ( n ) O(n) O(n))
定义窗口左端变量 left 并设为-1,窗口长度为当前遍历到的值 - left。
使用 enumerate(s) 遍历索引及对应的字符串,使用哈希表存放映射关系,可能有三种情况:
- 哈希表中没有当前字符:直接向哈希表中添加映射关系,判断当前窗口长度是否大于最大长度,若是则更改最大长度为当前窗口长度;
- 哈希表中已经有当前字符,原有字符包含在窗口中:先将窗口左端变量left移动到原有字符位置(如图红色括号),再更改字符索引为当前索引值,即 {‘b’: 2};
- 哈希表中已经有当前字符,原有字符不包含在窗口中:直接向哈希表中更改映射关系(如字符a),判断当前窗口长度是否大于最大长度,若是则更改最大长度为当前窗口长度。
因为1、3操作相同,可将2作为if条件,1、3作为else条件。
代码实现2:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
hash_map = {}
max_len = 0
left = -1
for i, c in enumerate(s):
if c in hash_map and hash_map.get(c) > left:
left = hash_map.get(c)
hash_map[c] = i
else:
hash_map[c] = i
max_len = max(max_len, i-left)
return max_len