Leetcode-无重复字符的最长字符串(滑动窗口)

132 阅读4分钟

暴力求解

class solution(object):
    def findMaxLength(self, s):
        n = len(s)
        max_length = 0
        for i in range(n):
            # set 是 Python 中的一种集合数据类型。集合是无序且不重复的元素的集合
            char_set = set()
            for j in range(i, n):
                if s[j] in char_set:
                    break
                char_set.add(s[j])
            max_length = max(max_length, j - i + 1)
        return max_length

set 是 Python 中的一种集合数据类型。集合是无序不重复的元素的集合

  • j - i + 1: 这部分计算的是当前子串的长度。j 是当前子串的结束位置,i 是当前子串的起始位置,所以 j - i 计算的是子串的长度。+ 1 是因为我们要考虑子串的最后一个字符。
  • max_length: 这是之前已经计算过的不含重复字符的最长子串的长度。
  • max(max_length, j - i + 1): 这是一个取最大值的操作。它比较当前计算得到的子串长度和之前已知的最大长度,然后选择较大的那个作为新的最大长度。

该算法的时间复杂度是 O(n^2)

滑动窗口

max_len = 0
        char_set = set()
        n = len(s)
        # 定义窗口的首尾端 (start, end),然后滑动窗口
        start = 0
        for end in range(n):
            # 如果当前字符已经在集合中,需要移动窗口的左指针
            while s[end] in char_set:
                char_set.remove(s[start])
                start += 1

            # 将当前字符加入集合
            char_set.add(s[end])

            # 更新最大长度
            max_len = max(max_len, end - start + 1)

        # 返回答案 (最大长度)
        return max_len

测试

sol = Solution() 
input_str = "abbc" 
result = sol.lengthOfLongestSubstring(input_str)
print(result)
  1. 右指针 rk 和左指针 i

    • rk 是右指针,用于扩展当前考虑的无重复字符子串的右边界。
    • i 是左指针,用于收缩当前考虑的无重复字符子串的左边界。
  2. 循环遍历字符串:

    • 外层循环由 for i in range(n) 组成,其中 i 是左指针。
    • 内层循环由 while rk + 1 < n and s[rk + 1] not in occ 组成,其中 rk 是右指针。
    • 在每次外层循环中,首先将左指针向右移动一格(移除一个字符),然后不断地将右指针向右移动,直到出现重复字符或者达到字符串末尾。
  3. 更新最大长度 ans

    • 在内层循环中,通过 ans = max(ans, rk - i + 1) 更新最大长度,确保 ans 始终存储的是当前找到的最长无重复字符子串的长度。

模板解法

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # Step 1: 定义需要维护的变量, 本题求最大长度,所以需要定义max_len, 该题又涉及去重,因此还需要一个哈希表
        max_len, hashmap = 0, {}

        # Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
        start = 0
        for end in range(len(s)):
            # Step 3
            # 更新需要维护的变量 (max_len, hashmap)
            # i.e. 把窗口末端元素加入哈希表,使其频率加1,并且更新最大长度
            # 尝试从哈希表中获取当前字符在哈希表中的值,如果哈希表中没有这个字符,则返回默认值0
            hashmap[s[end]] = hashmap.get(s[end], 0) + 1
            if len(hashmap) == end - start + 1:
                max_len = max(max_len, end - start + 1)
            
            # Step 4: 
            # 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
            # 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
            # 当窗口长度大于哈希表长度时候 (说明存在重复元素),窗口不合法
            # 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap)
            while end - start + 1 > len(hashmap):
                head = s[start]
                hashmap[head] -= 1
                if hashmap[head] == 0:
                    del hashmap[head]
                start += 1
        # Step 5: 返回答案 (最大长度)
        return max_len
sol = Solution() 
input_str = "abbc" 
result = sol.lengthOfLongestSubstring(input_str)
print(result)

动态演示1

模板解法

动态演示2