写在前面: 又到周一了,上周借助 最短子数组,我们一起学习了算法面试频率非常高的 “双指针法”,今天我们同样借助一个高频面试题 最长子串问题,来学习另一个非常重要的经典算法 “滑动窗口”。新的一周,新的开始,让我们开始本周的算法养成。小晶:【每周算法】长度最小的子数组zhuanlan.zhihu.com
题目描述
给定一个字符串,请你出其中不含有重复字符的最长子串的长度。难度:mid示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
题解
滑动窗口
首先解释下什么是滑动窗口。滑动窗口,顾名思义就是一个窗口从一侧滑动到另外一侧,并且绝不来回震动。也就是只遍历一此,每个元素最多访问两次(左右边均滑动过去),即时间复杂度为O(n)
。
了解了什么是滑动窗口,我们再来看本题。时间复杂度O(n)
空间复杂度O(∣Σ∣)
假设我们选择字符串中的第 k
个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r_k
。那么当我们选择第 k+1
个字符作为起始位置时,首先从 k+1
到 r_k
的字符显然是不重复的,并且由于少了原本的第 k
个字符,我们可以尝试继续增大 r_k
,直到右侧出现了重复字符为止。
class Solution:
# 我们使用两个变量表示字符串中的某个子串(左右边界)。
# 遍历字符串s,在每一步的迭代中,我们会将左指针向右移动一格,
# 表示 我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。
# 在移动结束后,这个子串就对应着 以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;
# 在枚举结束后,我们找到的最长的子串的长度即为答案。
def len_of_longest_sub_str_1(self, s: str) -> int:
# 哈希集合,记录每个字符是否出现过
right, max_sub_str_len = 0, 0
sub_s_set = set()
len_s = len(s)
for left in range(len_s):
if left != 0:
# 左指针向右移动一格,移除一个字符
sub_s_set.remove(s[left - 1])
while right < len_s and s[right] not in sub_s_set:
# 不断地移动右指针
sub_s_set.add(s[right])
right += 1
# 第 left 到 right 个字符是一个极长的无重复字符子串
max_sub_str_len = max(max_sub_str_len, right - left)
return max_sub_str_len
时间复杂度很好解释,我们来理解下空间复杂度O(∣Σ∣)
。其中 Σ
表示字符集(即字符串中可以出现的字符),∣Σ∣
表示字符集的大小。因为我们要用set
来存储出现过的字符,而字符最多有∣Σ∣
个,因此空间复杂度为 (∣Σ∣)
。
HashMap 优化
看到len_of_longest_sub_str_1
这个函数名你们一定能够猜到,这么经典的题,一定不止探究一种解法。没错,其实本题完全可以不用set
,我只是想借本题让大家掌握滑动窗口。
学会滑动窗口,您可以高兴的离开了,但如果恰好您也了解C++/Java中的hashmap
,这里我们以hashmap
思想用Python实现(python没有hashmap
,但知识是相通的,再一次感叹Python的易用程度),我们会看到Python不仅简化了代码,就连空间复杂度也降低了。时间复杂度O(n)
空间复杂度O(1)
def len_of_longest_sub_str_2(self, s: str) -> int:
left, sub_str_len, max_sub_str_len = 0, 0, 0
len_s = len(s)
for right in range(len_s):
# 仅当s[left, right) 中存在s[right]时更新left
temp_char_index = s.index(s[right], left, right + 1)
if temp_char_index != right:
left = temp_char_index + 1
sub_str_len = right - left
sub_str_len += 1
max_sub_str_len = max(max_sub_str_len, sub_str_len)
return
太棒了,本周还没开始,我们的每周算法任务就完成了!最后分享一个坚持的动力:
无志之人常立志,有志之人立长志
欢迎大家关注我,和我一起坚持学习【每周算法】,直到将坚持养成像吃饭喝水一样的习惯。另外有问题大家评论区讨论,积极沟通。如果本文对你有帮助,不要忘记点赞或收藏,克服拖延,拒绝伸手党!