从0开始学算法——第十五天(滑动窗口练习)

写在开头的话

学习了今天的基础知识,让我们来做几道题来练练手吧。如果有不懂的可以看看写在文末的思路讲解(题目是别的地方扒来的,参考答案是我自己写的,肯定不是最优解,有更好的方法欢迎评论区交流)

题目一------最长不重复子串

题目二------全部都有的子序列

题目三------最少的数

参考答案

第一题参考答案(Python版)

python 复制代码
def lengthOfLongestSubstring(s: str) -> int:
    char_index = {}  # 记录字符最近出现的位置
    left = 0  # 窗口左边界
    max_len = 0
    
    for right, ch in enumerate(s):
        # 如果字符在窗口内出现过,移动左指针
        if ch in char_index and char_index[ch] >= left:
            left = char_index[ch] + 1
        # 更新字符的最新位置
        char_index[ch] = right
        # 更新最大长度
        max_len = max(max_len, right - left + 1)
    
    return max_len


if __name__ == "__main__":
    s = input().strip()
    print(lengthOfLongestSubstring(s))

第二题参考答案(Python版)

python 复制代码
def min_blue_bridge_length():
    import sys
    
    # 读取输入
    n = int(sys.stdin.readline().strip())
    arr = list(map(int, sys.stdin.readline().strip().split()))
    
    # 获取所有不同的数字
    unique_nums = set(arr)
    total_unique = len(unique_nums)
    
    if total_unique == 0:
        return 0
    
    left = 0
    min_length = n  # 初始化为最大可能长度
    counter = {}  # 记录当前窗口中每个数字出现的次数
    found = 0  # 记录当前窗口中已经找到的不同数字数量
    
    for right in range(n):
        # 右指针向右移动,扩大窗口
        num = arr[right]
        counter[num] = counter.get(num, 0) + 1
        
        # 如果是第一次遇到这个数字
        if counter[num] == 1:
            found += 1
        
        # 当窗口中包含所有不同数字时,尝试缩小窗口
        while found == total_unique and left <= right:
            # 更新最小长度
            min_length = min(min_length, right - left + 1)
            
            # 移动左指针,缩小窗口
            left_num = arr[left]
            counter[left_num] -= 1
            
            # 如果某个数字的计数变为0,说明窗口中不再包含该数字
            if counter[left_num] == 0:
                found -= 1
            
            left += 1
    
    return min_length

# 示例测试
if __name__ == "__main__":
    print(min_blue_bridge_length())

第三题参考答案(Python版)

python 复制代码
import sys

def main():
    data = sys.stdin.read().strip().split()
    if not data:
        return
    n, d = map(int, data[:2])
    a = list(map(int, data[2:2 + n]))

    # 特判:如果 d 为 1,则最小不同元素数至少为 1(除非数组为空)
    if d == 1:
        print(1)
        return

    # 使用字典记录当前窗口中每个元素的出现次数
    freq = {}
    unique = 0  # 当前窗口内不同元素的数量

    # 初始化第一个窗口 [0, d-1]
    for i in range(d):
        freq[a[i]] = freq.get(a[i], 0) + 1
        if freq[a[i]] == 1:
            unique += 1

    min_unique = unique  # 记录最小值

    # 滑动窗口:每次向右移动一位
    for i in range(d, n):
        # 移除窗口最左侧的元素 a[i-d]
        left_val = a[i - d]
        freq[left_val] -= 1
        if freq[left_val] == 0:
            unique -= 1
        # 添加新元素 a[i]
        right_val = a[i]
        freq[right_val] = freq.get(right_val, 0) + 1
        if freq[right_val] == 1:
            unique += 1
        # 更新最小值
        if unique < min_unique:
            min_unique = unique

    print(min_unique)

if __name__ == "__main__":
    main()

思路讲解

题目一------最长不重复子串

滑动窗口通常用两个指针(或索引)来表示当前考虑的子串的左右边界。我们用一个集合(或字典)来记录当前窗口中的字符,以保证没有重复字符。具体步骤如下:

  1. 初始化左指针left=0,右指针right=0,最大长度max_len=0,以及一个集合(或字典)用于记录当前窗口中的字符。

  2. 右指针不断向右移动,每次将右指针指向的字符加入当前窗口。如果加入后窗口中没有重复字符,则更新最大长度。

  3. 如果加入后窗口中出现重复字符,则移动左指针,直到窗口中没有重复字符为止。

  4. 重复步骤2和3,直到右指针到达字符串末尾。

题目二------全部都有的子序列

具体步骤:

  1. 统计所有不同的数字:使用集合获取所有不同的数字,确定目标子数组必须包含这些数字。

  2. 滑动窗口

    • 使用两个指针 leftright 表示当前窗口的左右边界

    • 使用字典 counter 记录当前窗口中每个数字的出现次数

    • 使用变量 found 记录当前窗口中已经找到的不同数字数量

  3. 移动右指针

    • 扩大窗口,增加 counter 中的计数

    • 当某个数字第一次出现在窗口中时,found 加 1

  4. 移动左指针

    • found == 总不同数字数 时,尝试缩小窗口

    • 移动左指针,减少对应数字的计数

    • 如果某个数字的计数变为 0,说明窗口中不再包含该数字,found 减 1

  5. 记录最小长度

    • 每次窗口满足条件时,记录当前窗口长度,并更新最小值

题目三------最少的数

思路:我们可以用滑动窗口来统计每个长度为d的窗口内不同元素的个数,然后取最小值。

具体步骤:

  1. 使用一个哈希表(字典)来记录当前窗口内每个元素出现的次数。

  2. 初始化第一个窗口(从0到d-1)的元素计数,并统计不同元素的个数。

  3. 然后从索引d开始,每次移动窗口:移除窗口最左边的元素(索引i-d),加入当前元素(索引i),并更新计数和不同元素的个数。

    • 如果移除后某个元素的计数变为0,则不同元素个数减1。

    • 如果加入的元素之前计数为0,则不同元素个数加1。

  4. 在每次移动窗口后,记录当前窗口不同元素的数量,并更新最小值。

注意:由于n和d最大为,a[i]最大为,所以可以用字典来计数,时间复杂度O(n),空间复杂度O(d)(存储窗口内元素计数)。

但是注意:d可能等于n,所以窗口大小最大为n。

相关推荐
DuHz7 小时前
milliLoc 论文精读:把商用毫米波 FMCW 的绝对测距从“厘米栅格”推进到“毫米级连续值”,并顺带修正 AoA 的系统相位偏差
论文阅读·物联网·算法·信息与通信·毫米波雷达
江苏世纪龙科技7 小时前
开启汽车实训新维度:基于真实标准的虚拟仿真教学软件
学习
逐辰十七7 小时前
freertos学习笔记12--个人自用-第16章 软件定时器(software timer)
笔记·学习
玩具猴_wjh7 小时前
12.13 学习笔记
笔记·学习
雾岛听风眠7 小时前
运放学习笔记
笔记·学习
肥大毛7 小时前
C++入门学习---结构体
开发语言·c++·学习
likeshop 好像科技7 小时前
新手学习AI智能体Agent逻辑设计的指引
人工智能·学习·开源·github
qq_401700417 小时前
Linux文件锁解决多进程并发
linux·服务器·算法
长安er8 小时前
LeetCode 83/237/82 链表删除问题-盒子模型
数据结构·算法·leetcode·链表·力扣