刷题:力扣热题100--滑动窗口(Day03)

文章目录

  • [1. Problem 1 无重复字符的最长字串(# 3)](# 3))
    • [1.1 题目表述](#1.1 题目表述)
    • [1.2 题解](#1.2 题解)
  • [2. Problem 2 找到字符串中所有字母异位词(# 438)](# 438))
    • [2.1 题目表述](#2.1 题目表述)
    • [2.2 题解](#2.2 题解)
  • [3. 总结](#3. 总结)

1. Problem 1 无重复字符的最长字串(# 3)

1.1 题目表述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串的长度。

示例 1:

plain 复制代码
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。

示例 2:

plain 复制代码
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

plain 复制代码
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 10(4)

  • s 由英文字母、数字、符号和空格组成

1.2 题解

本题本质上是找出最长的无重复字符的子串,而针对是否重复问题,最快的搜索方法是哈希表,针对子串范围问题最优处理思路是双指针,因此本题适合使用滑动窗口法(哈希表/数组+双指针)进行处理。其伪代码如下:

plain 复制代码
// 输入:字符串 s
// 输出:无重复字符的最长子串长度

// 1. 初始化
left = 0
max_len = 0
// 哈希表:key = 字符,value = 字符最后一次出现的索引
char_map = 空哈希表

// 2. 右指针遍历字符串
for right from 0 to len(s)-1:
    current_char = s[right]
    
    // 如果当前字符已在窗口中出现过,更新左指针
    if current_char in char_map and char_map[current_char] >= left:
        left = char_map[current_char] + 1
    
    // 更新当前字符的最后出现位置
    char_map[current_char] = right
    
    // 计算当前窗口长度,更新最大长度
    current_len = right - left + 1
    if current_len > max_len:
        max_len = current_len

// 3. 返回结果
return max_len
  • 哈希表:记录当前已经出现的字符以及每个字符最近出现的位置

  • 左指针:滑动窗口左边界,保证窗口中没有重复元素

  • 右指针:滑动窗口右边界,遍历寻找更长无重复子串

  • 指针之间关系:右指针始终向右移动,左指针根据右指针遍历信息若遇到重复字符则跟着向右移动,记录滑动窗口(两指针间)的长度,并寻找最大值。

2. Problem 2 找到字符串中所有字母异位词(# 438)

2.1 题目表述

给定两个字符串 sp,找到 s中所有 p的 异位词的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

示例 1:

plain 复制代码
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例 2:

plain 复制代码
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 10(4)

  • sp 仅包含小写字母

2.2 题解

本题需要找 sp 的所有异位词的位置,这要求:1)子串长度相同;2)符合异位词特征(字符出现频率或重排后完全一致),由于子串的长度要求是固定的,因此滑动窗口的大小也就是恒定的了,因此本题中指针的概念便也随之弱化。另外由于本题涉及异位词的相关问题,因此可以参考 字母异位词分组(# 49) 题的思路进行判断。伪代码可表示为:

plain 复制代码
// 输入:字符串 s, p
// 输出:所有异位词子串的起始索引列表

len_s = length(s)
len_p = length(p)
结果列表 result = 空列表

// 边界情况:s比p短,直接返回空
if len_s < len_p:
    return result

// 1. 初始化字符计数数组(26个小写字母)
count_p = [0] * 26
count_s = [0] * 26

// 2. 统计p的字符计数
for i from 0 to len_p-1:
    count_p[ ord(p[i]) - ord('a') ] += 1
    count_s[ ord(s[i]) - ord('a') ] += 1

// 3. 检查初始窗口是否匹配
if count_s == count_p:
    add 0 to result

// 4. 滑动窗口遍历剩余部分
for left from 1 to len_s - len_p:
    right = left + len_p - 1
    
    // 移出左边界前一个字符
    count_s[ ord(s[left-1]) - ord('a') ] -= 1
    // 移入新的右边界字符
    count_s[ ord(s[right]) - ord('a') ] += 1
    
    // 检查当前窗口是否匹配
    if count_s == count_p:
        add left to result

// 5. 返回结果
return result

在这里,我们考虑了边界情况(p长度大于s长度),在实际应用中,进行边界情况的考虑检验测试十分重要。

3. 总结

滑动窗口的关键,不只是"维护一个区间",而是学会把原本静态的子串问题,转化为一个动态更新的过程。无重复字符的最长子串,本质是在不断扩展与收缩窗口中维持"区间合法";而寻找异位词,则是在固定窗口大小下持续比较字符分布。两道题看似形式不同,但都体现了滑动窗口对"连续区间问题"的高效处理能力:避免每次从头判断,而是在移动中完成更新与筛选。

更深一层看,滑动窗口体现的是一种过程化思维:问题的答案不是一次性算出来的,而是在窗口推进中逐步逼近的。它要求我们时刻思考窗口内哪些信息需要保留、哪些需要剔除,以及边界变化会带来什么影响。尤其在异位词问题中,对边界情况的重视也提醒我们,真正成熟的解题能力,不只在于掌握方法,更在于对细节、约束和有效性的持续把控。

相关推荐
郝学胜-神的一滴2 小时前
深入解析:生成器在UserList中的应用与Python可迭代对象实现原理
开发语言·python·程序人生·算法
Yzzz-F2 小时前
Problem - 2157D - Codeforces
算法
颜酱2 小时前
回溯算法实战练习(2)
javascript·后端·算法
We་ct2 小时前
LeetCode 153. 旋转排序数组找最小值:二分最优思路
前端·算法·leetcode·typescript·二分·数组
享哥2 小时前
tick 数据探索笔记:从抓取到理解
算法
禹笑笑-AI食用指南2 小时前
本地脱敏:把数据安全控制在自己手里
算法·ai·脱敏
JosieBook2 小时前
【C#】C# 中的 enum、struct 和 class 对比总结
开发语言·算法·c#
拾荒的路由2 小时前
HOT100DAY9记录用
数据结构·算法·leetcode
沙雕不是雕又菜又爱玩2 小时前
leetcode第7题 整数反转(C++)
算法·leetcode