文章归档算法专题

leetcode滑动窗口练习题

滑动窗口适合处理一部分连续子串问题。穷举子串需要枚举起点和终点,仅区间数量就有 O(n2)O(n^2),逐段检查还会增加开销。若能随着左右边界移动增量维护窗口状态,就可以避免反复检查整段子串。虽然代码里可能嵌套两个循环,但左右指针都只向前移动;窗口数据的单次更新为常数开销时,整体时间复杂度为 O(n)O(n)。

下面是滑动窗口的伪代码框架,其中的省略内容需要根据题目补全:

Python
# 滑动窗口算法伪码框架
def slidingWindow(s: str):
    # 用合适的数据结构记录窗口中的数据,根据具体场景变通
    # 比如说,我想记录窗口中元素出现的次数,就用 map
    # 如果我想记录窗口中的元素和,就可以只用一个 int
    window = ...

    left, right = 0, 0
    while right < len(s):
        # c 是将移入窗口的字符
        c = s[right]
        window.add(c)
        # 增大窗口
        right += 1
        # 进行窗口内数据的一系列更新
        ...

        # *** debug 输出的位置 ***
        # 注意在最终的解法代码中不要 print
        # 因为 IO 操作很耗时,可能导致超时
        # print(f"window: [{left}, {right})")
        # ***********************

        # 判断左侧窗口是否要收缩
        while left < right and window needs shrink:
            # d 是将移出窗口的字符
            d = s[left]
            window.remove(d)
            # 缩小窗口
            left += 1
            # 进行窗口内数据的一系列更新
            ...

写滑动窗口时,我会先考虑三个问题:

1、什么时候移动 right 扩大窗口?新字符加入后,需要更新哪些数据?

2、什么时候移动 left 缩小窗口?字符移出后,需要更新哪些数据?

3、在哪个时机更新答案?

这些问题能帮助确定窗口的维护方式。还需要结合题目条件判断:边界是否可以只向前移动,移除或加入元素后,窗口状态能否及时更新。

76. 最小覆盖子串

这道题用 need 统计目标串中每个字符需要出现的次数,用 window 统计当前窗口的字符次数。代码中的 vaild 记录已有多少种字符达到了所需次数,left 和 right 则表示窗口的左右边界。满足所有字符要求后,尝试缩小窗口,并记录更短的可行答案。

Python
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        window = dict()
        need = dict()
# 把目标串存到目标窗口
        for i in t:
            need[i] = need.get(i, 0) + 1
        vaild = 0
        left = right = 0
        length = float("inf")
        res = ""
        while right < len(s):
            c = s[right]
            right += 1
# 如果字符在need窗口里面就让计数器+1
            if c in need:
                window[c] = window.get(c, 0) + 1
# 如果这个字符满足条件了,就让vaild+1
                if window[c] == need[c]:
                    vaild += 1
# 当vaild和目标窗口长度相同,就代表所有元素都满足条件了,尝试缩小窗口
            while vaild == len(need):
# 此时的子串就是一个符合条件的子串,尝试更新答案
                if length > right - left:
                    res = s[left:right]
                    length = right - left
                c = s[left]
                left += 1
# 缩小窗口,更新相关的数据。
                if c in need:
                    if window[c] == need[c]:
                        vaild -= 1
                    window[c] -= 1
        return res

567. 字符串的排列

这里维护的是长度等于 s1 的定长窗口。内层我用了 if:每加入一个字符,窗口长度最多比目标长度多一格,检查后移出最左侧一个字符即可。当窗口长度达到 s1 的长度时,如果每种字符的数量都满足要求,就找到了 s1 的一种排列;否则继续向右滑动。

Python
class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        need = dict()
        window = dict()
        for i in s1:
            need[i] = need.get(i, 0) + 1
        vaild = left = right = 0
        while right < len(s2):
            c = s2[right]
            right += 1
            if c in need:
                window[c] = window.get(c, 0) + 1
                if window[c] == need[c]:
                    vaild += 1
            if right - left >= len(s1):
                if vaild == len(need):
                    return True
                c = s2[left]
                left += 1
                if c in need:
                    if window[c] == need[c]:
                        vaild -= 1
                    window[c] -= 1
        return False

438. 找到字符串中所有字母异位词

思路与第 567 题相同,仍然维护定长窗口。区别是不能找到一个就返回,而要把每个符合条件的窗口起始索引加入结果。

Python
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        need = dict()
        windows = dict()
        vaild = left = right = 0
        res = []
        for i in p:
            need[i] = need.get(i, 0) + 1
        while right < len(s):
            c = s[right]
            right += 1
            if c in need:
                windows[c] = windows.get(c, 0) + 1
                if windows[c] == need[c]:
                    vaild += 1
            if right - left >= len(p):
                if vaild == len(need):
                    res.append(left)
                c = s[left]
                left += 1
                if c in need:
                    if windows[c] == need[c]:
                        vaild -= 1
                    windows[c] -= 1
        return res

3. 无重复字符的最长子串

本题不需要 need,只统计当前窗口中各字符的次数。每次加入新字符后,如果它出现了不止一次,就移动左边界,直到重复被消除。内层 while 结束时,当前窗口不含重复字符,再用窗口长度更新答案。

Python
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        left = right = 0
        window = dict()
        length = 0
        while right < len(s):
            c = s[right]
            right += 1
            window[c] = window.get(c, 0) + 1
            while window[c] >= 2:
                new_c = s[left]
                left += 1
                window[new_c] -= 1
            if right - left > length:
                length = right - left
        return length
谢谢你读到这里。 ✧
搜索文章

输入关键词,找一篇想读的手记。

算法专题leetcode双指针经典练习题算法题解leetcode-1438. 绝对差不超过限制的最长连续子数组算法题解leetcode-179. 最大数算法题解leetcode-239. 滑动窗口最大值算法题解leetcode-136. 只出现一次的数字算法专题leetcode动态规划基础练习题算法专题leetcode二叉树基础练习题算法专题leetcode数组双指针练习题算法专题leetcode滑动窗口练习题算法专题leetcode链表双指针练习题算法题解洛谷-P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题算法题解leetcode-1979. 找出数组的最大公约数算法题解洛谷-B3634 最大公约数和最小公倍数算法题解洛谷-P10424 [蓝桥杯 2024 省 B] 好数算法题解洛谷-P8716 [蓝桥杯 2020 省 AB2] 回文日期自动化脚本Dr.com校园网自动登录方案算法题解leetcode-2226. 每个小孩最多能分到多少糖果算法题解leetcode-2563. 统计公平数对的数目算法题解leetcode-1.两数之和算法题解洛谷-P8218 【深进1.例1】求区间和
在这座小岛上,慢慢找。ESC 关闭