• 算法通关村第十六关:白银挑战-滑动窗口经典问题


    白银挑战-滑动窗口经典问题

    1. 最长子串专题

    1.1 无重复字符的最长子串

    LeetCode3

    思路分析

    要找最长子串,需要直到无重复字符串的首和尾,然后再确定最长的那个,需要两个指针,可利用滑动窗口思想

    方法1:集合

    • 建立一个集合用于存储无重复的字符
    • 建立滑动窗口的起始位置 left,right
    • right向前滑动
      如果遇到相同元素,left向前滑动并删除集合中对应的元素,直到相同元素被删除,然后再将right对应元素保存至集合中
      如果遇到不同元素,left不变,将right对应元素保存至集合中
    • 一边滑动一边比较集合长度和已知最大长度,最大长度取比较的较大值
    • 字符遍历完成,输出最大长度

    方法2:map

    • 定义一个 K-V 形式的map,key表示字符串,value表示其下标索引值
    • 定义两个指针left 和 right,left表示无重复字符串首部索引,right为尾部索引
    • 遍历字符串,每访问一个字符,都将更新map,将字符串下标索引值更新成最新的
      如果遇到重复元素,更新对应下标索引,同时更新left,left取原left值和重复元素索引值+1二者最大值(特殊情况 abba, b先重复,a后重复,防止left倒流)
    • 一边滑动一边比较right-left长度和已知最大长度,最大长度取比较的较大值
    • 字符遍历完成,输出最大长度

    代码实现

    方法1:集合

    class Solution:
        def lengthOfLongestSubstring(self, s: str) -> int:
            if not s:
                return 0
            char_set = set()
            left = 0
            max_len = 0
            for right in range(len(s)):
                while s[right] in char_set:
                    char_set.remove(s[left])
                    left += 1
                char_set.add(s[right])
                max_len = max(max_len, len(char_set))
    
            return max_len
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    方法2:map

    class Solution:
        def lengthOfLongestSubstring(self, s: str) -> int:
            if len(s) == 0:
                return 0
            char_map = {}
            max_len = 0
            left = 0
            for right in range(len(s)):
                if s[right] in char_map:
                    left = max(left, char_map[s[right]] + 1)
                char_map[s[right]] = right
                max_len = max(max_len, right - left + 1)
            return max_len
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1.2 至多包含两个不同字符的最长子串

    LeetCode159 至多包含两个不同字符的最长子串
    给你一个字符串 s ,请你找出 至多 包含 两个不同字符 的最长子串,并返回该子串的长度。
    https://leetcode.cn/problems/longest-substring-with-at-most-two-distinct-characters/

    思路分析

    仍然使用 left 和 right 来锁定一个窗口,然后一边向右移动一边分析

    我们接下来需要解决两个问题

    1. 怎么判断只有两个元素
    2. 移除时怎么知道移除谁,以及移除之后的left是什么

    怎么判断只有两个元素?
    还是hash好用,每一个时刻,hashmap包括不超过3个元素

    判断移除谁,移除后left是什么?
    设计一下hashmap的key-value的含义,key:字符,value:窗口中最右边的字符位置

    del_idx = min(hashmap.values())
    del hashmap[s[del_idx]]
    left = del_idx + 1
    
    • 1
    • 2
    • 3

    代码实现

    class Solution:
        def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
            if len(s) < 3:
                return len(s)
            char_map = {}
            max_len = 0
            left = 0
            for right in range(len(s)):
                if len(char_map) < 3:
                    char_map[s[right]] = right
                if len(char_map) == 3:
                    del_idx = min(char_map.values())
                    del char_map[s[del_idx]]
                    left = del_idx + 1
                max_len = max(max_len, right - left + 1)
    
            return max_len
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1.3 至多包含K个不同字符的最长子串

    LeetCode340
    https://leetcode.cn/problems/longest-substring-with-at-most-k-distinct-characters/description/

    思路分析
    与上一题类似,只要把2改为k即可

    代码实现

    class Solution:
        def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int:
            if len(s) < k+1:
                return len(s)
            char_map = {}
            max_len = 0
            left = 0
            for right in range(len(s)):
                if len(char_map) < k+1:
                    char_map[s[right]] = right
                if len(char_map) == k+1:
                    del_idx = min(char_map.values())
                    del char_map[s[del_idx]]
                    left = del_idx + 1
                max_len = max(max_len, right - left + 1)
    
            return max_len
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2. 长度最小的子数组

    LeetCode209
    https://leetcode.cn/problems/minimum-size-subarray-sum/

    思路分析

    本题可以使用双指针来解决,也可视为队列法

    基本思路如下
    先让元素不断入队,当入队元素和大于等于target是记录一下此时队列的容量
    如果队列元素之和大于等于target则开始出队,直到小于target再入队

    代码实现

    class Solution:
        def minSubArrayLen(self, target: int, nums: List[int]) -> int:
            left = 0
            min_len = len(nums) + 1
            total = 0
    
            for right in range(len(nums)):
                total += nums[right]
                while total >= target:
                    min_len = min(min_len, right - left + 1)
                    total -= nums[left]
                    left += 1
            
            return min_len if min_len <= len(nums) else 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3. 盛水最多的容器

    LeetCode11
    https://leetcode.cn/problems/container-with-most-water/

    思路分析

    盛水的体积的计算:短板的高度*长短板之间的宽度
    S(left, right) = min(h[left], h[right])(right - left)

    长短板分列最左边和最右边,宽度最大,此时长板或短板向中间收窄,宽度会变小,此时板的高度

    • 若短板向中间收窄,短板可能变大,水槽面积可能增加
    • 若长板向中间收窄,短板可能变小或不变,水槽面积一定变小

    具体执行

    • 初始双指针分列水槽的左右两端
    • 循环每轮将短板向内移动一格
    • 更新面积的最大值,直到两指针相遇时跳出,即可获得最大面积

    代码实现

    class Solution:
        def maxArea(self, height: List[int]) -> int:
            left, right = 0, len(height)-1
            s = 0
            while left < right:
                s = max(s, min(height[left], height[right]) * (right - left))
                if height[left] < height[right]:
                    left += 1
                else:
                    right -= 1
            return s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4. 寻找子串异位词(排列)

    异位词(排列)
    如果两个字符串仅仅是字母出现的位置不一样,则称两者互为对方的一个排列,也称异位词

    如何判断?

    • 字母的类型一样
    • 每个字母出现的个数也是一样的
    4.1 字符串的排列

    LeetCode 567
    https://leetcode.cn/problems/permutation-in-string/

    思路分析

    • 字符串的异位词长度 和 字符串的长度是样的,所以以字符串的长度作为滑动窗口的大小
    • 窗口一边右移,一边比较
    • 如何判断异位词,排序的代价太高了,可以创建一个大小为26的数组,每个位置存储从a到z的个数
      数组索引使用 index = s1.charAt(i) - ‘a’ 来表示
    • 窗口右移:charArray[s2.charAt(right) - ‘a’]++
    • 窗口左移:int left = right - s1.length; charArray[s2.charAt(left) - ‘a’]–

    代码实现

    python中字母与ascii转换 chr() ord()

    class Solution:
        def checkInclusion(self, s1: str, s2: str) -> bool:
            n1, n2 = len(s1), len(s2)
            if n1 > n2:
                return False
            char_arr1 = [0] * 26
            char_arr2 = [0] * 26
            for i in range(n1):
                char_arr1[ord(s1[i]) - ord('a')] += 1
                char_arr2[ord(s2[i]) - ord('a')] += 1
    
            if char_arr1 == char_arr2:
                return True
    
            left = 0
            for right in range(n1, n2):
                char_arr2[ord(s2[right]) - ord('a')] += 1
                char_arr2[ord(s2[left]) - ord('a')] -= 1
                left += 1
                if char_arr1 == char_arr2:
                    return True
    
            return False
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    4.2 找到字符串中所有字母异位

    LeetCode438
    https://leetcode.cn/problems/find-all-anagrams-in-a-string/

    思路分析
    思路分析同上题一致,多加了一步进行处理

    增加了一个列表,记录出现异位词的位置,如果出现了,将其加到列表中

    代码实现

    class Solution:
        def findAnagrams(self, s: str, p: str) -> List[int]:
            n1, n2 = len(p), len(s)
            if n1 > n2:
                return []
            res = []
            char_arr1 = [0] * 26
            char_arr2 = [0] * 26
            for i in range(n1):
                char_arr1[ord(p[i]) - ord('a')] += 1
                char_arr2[ord(s[i]) - ord('a')] += 1
    
            if char_arr1 == char_arr2:
                res.append(0)
    
            left = 0
            for right in range(n1, n2):
                char_arr2[ord(s[right]) - ord('a')] += 1
                char_arr2[ord(s[left]) - ord('a')] -= 1
                left += 1
                if char_arr1 == char_arr2:
                    res.append(left)
    
            return res
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    class Solution:
        def findAnagrams(self, s: str, p: str) -> List[int]:
            p_len, s_len = len(p), len(s)
            if p_len > s_len:
                return []
            res = []
            p_count = [0] * 26
            s_count = [0] * 26
            for i in range(p_len):
                p_count[ord(p[i]) - ord('a')] += 1
                s_count[ord(s[i]) - ord('a')] += 1
    
            if p_count == s_count:
                res.append(0)
    
            for i in range(s_len - p_len):
                s_count[ord(s[i + p_len]) - ord('a')] += 1
                s_count[ord(s[i]) - ord('a')] -= 1
                if p_count == s_count:
                    res.append(i + 1)
    
            return res
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    欧拉道路/回路总结
    【01】弄懂共识机制PoW
    清华系激光雷达公司,成了量产元年最大的黑马
    从程序员的角度看人类通信史
    校园新闻网站|基于SpringBoot+ Mysql+Java+B/S结构的校园新闻网站设计与实现(可运行源码+数据库+设计文档+部署说明)
    玩转Mysql系列 - 第17篇:存储过程&自定义函数详解
    基于SSM的网络安全宣传网站设计与实现
    《银河麒麟高级服务器操作系统V10》使用
    谐振波导光栅的严格分析
    Servlet
  • 原文地址:https://blog.csdn.net/qq_41662142/article/details/132730331