当前位置: 首页 > news >正文

力扣hot100:无重复字符的最长子串,找到字符串中所有字母异位词(滑动窗口算法讲解)(3,438)

 问题描述

️ 核心思路:滑动窗口

我们使用 双指针 维护一个动态窗口 [left, right]

  1. 右指针 right 向前探索,将新字符纳入窗口
  2. 左指针 left 在发现重复时收缩窗口
  3. 哈希集合 set 实时记录窗口内的字符,实现 O(1) 重复检测

整个过程如同一个伸缩的窗口在字符串上滑动,始终保持窗口内无重复字符:

示例:s = "abcabcbb"
Step1: [a]bcabcbb  → set=[a]   len=1
Step2: [ab]cabcbb  → set=[a,b] len=2
Step3: [abc]abcbb  → set=[a,b,c] len=3
Step4: [abca]bcbb → 重复! 收缩左边界 → a[bca]bcbb → set=[b,c,a] len=3
...

代码实现(逐行解析)
class Solution {public int lengthOfLongestSubstring(String s) {Set<Character> set = new HashSet<>();  // 存储窗口内的字符int result = 0;                        // 记录最长长度// 双指针初始化:left固定左边界,right探索右边界for (int left = 0, right = 0; right < s.length(); right++) {// 关键步骤:发现重复字符时,持续收缩左边界while (set.contains(s.charAt(right))) {set.remove(s.charAt(left));    // 移除左边界字符left++;                        // 左指针右移}set.add(s.charAt(right));          // 将新字符加入窗口result = Math.max(result, right - left + 1); // 更新最大值}return result;}
}

关键操作解析

  1. set.contains(s.charAt(right)) O(1) 复杂度检测新字符是否重复
  2. set.remove(s.charAt(left++)) 循环收缩左边界直到消除重复
  3. right - left + 1 实时计算当前无重复窗口长度
️ 复杂度分析
  • 时间复杂度 O(n) 虽然嵌套循环,但每个字符最多被 left 和 right 各访问一次(均摊 O(1))
  • 空间复杂度 O(∣Σ∣) 字符集大小决定哈希表空间(ASCII 字符集为 O(128))
 总结与思考
  1. 为什么用 while 而不是 if 当新字符与窗口中多个字符冲突时(如 "abba"),必须持续收缩直到完全消除重复。
  2. 滑动窗口的本质 通过动态调整左右边界,将暴力解法的 O(n²) 优化至 O(n),是处理连续区间问题的利器。
  3. 拓展变种 若题目改为允许重复一次(如 LeetCode 424),只需将集合替换为哈希映射记录频次,稍作调整即可解决。

=========================================================================

题目描述

解题思路

字母异位词的核心特征是字符种类和数量完全相同,只是顺序不同。因此,我们可以通过比较字符频率来判断是否为异位词。具体步骤如下:

  1. 初始化频率数组:创建长度为 26 的数组 pCount,统计字符串 p 中每个字母的出现次数。
  2. 滑动窗口遍历:在字符串 s 上维护一个固定长度为 p.length() 的窗口:
    • 每次将右边界字符加入窗口(对应计数加 1)。
    • 当窗口大小超过 p.length() 时,移除左边界字符(对应计数减 1)。
    • 比较当前窗口的字符频率与 pCount,若相等则记录起始索引。
  3. 返回结果:收集所有满足条件的起始索引。

算法特点

  • 时间复杂度:O(n),其中 n 是字符串 s 的长度。每个字符进入和离开窗口各一次,频率比较耗时 O(26) ≈ O(1)。
  • 空间复杂度:O(1),仅使用固定大小的频率数组(长度 26)。

代码实现

class Solution {public List<Integer> findAnagrams(String s, String p) {List<Character> s_list = s.chars().mapToObj(c->(char)c).collect(Collectors.toList());List<Character> p_list = p.chars().mapToObj(c->(char)c).collect(Collectors.toList());List<Integer> result=new ArrayList<>();int length=s_list.size();// 改进的滑动窗口实现int pLen = p_list.size();if (s_list.size() < pLen) return result;// 创建字符频率数组int[] pCount = new int[26];int[] windowCount = new int[26];// 统计p中字符频率for (char c : p_list) {pCount[c - 'a']++;}// 滑动窗口for (int i = 0; i < s_list.size(); i++) {// 添加右边字符到窗口windowCount[s_list.get(i) - 'a']++;// 如果窗口大小超过p的长度,移除左边字符if (i >= pLen) {windowCount[s_list.get(i - pLen) - 'a']--;}// 比较窗口和p的字符频率if (Arrays.equals(pCount, windowCount)) {result.add(i - pLen + 1);}}return result;}
}

关键点解析

  1. 频率数组替代排序:避免对每个子串排序(O(p log p)),直接比较字符频率(O(26))。
  2. 滑动窗口边界处理
    • 当 i >= pLen 时,窗口长度超过 p,需移除左边界的字符。
    • 仅当 i >= pLen - 1 时,窗口长度达到 p 的长度,才进行频率比较。
  3. 索引计算:起始索引为 i - pLen + 1(当前右边界索引减去窗口长度加 1)。

示例分析

s = "cbaebabacd", p = "abc" 为例:

  1. 初始化 pCount = [1,1,1,0,...](a/b/c 各出现 1 次)。
  2. 窗口滑动过程:
    • 窗口 "cba" → 频率 [1,1,1,...] → 匹配 → 记录索引 0。
    • 窗口 "bae" → 频率 [1,1,0,...] → 不匹配。
    • 窗口 "bab" → 频率 [1,2,0,...] → 不匹配。
    • ...
    • 窗口 "bac" → 频率 [1,1,1,...] → 匹配 → 记录索引 6。

最终返回结果 [0, 6]

总结

通过滑动窗口结合字符频率统计,我们高效解决了字母异位词的搜索问题。该方法避免了冗余计算,显著提升了性能,是处理同类字符串匹配问题的经典思路。

http://www.dtcms.com/a/341946.html

相关文章:

  • LeetCode每日一题,2025-08-21
  • C++——C++重点知识点复习2(详细复习模板,继承)
  • 2.Shell脚本修炼手册---创建第一个 Shell 脚本
  • C++ string类(reserve , resize , insert , erase)
  • 鸿蒙中网络诊断:Network分析
  • 深入理解JVM内存结构:从字节码执行到垃圾回收的全景解析
  • 金山云Q2营收23.5亿元 AI战略激活业务增长新空间
  • Altium Designer 22使用笔记(8)---PCB电气约束设置
  • GitHub Copilot - GitHub 推出的AI编程助手
  • Pytorch框架学习
  • Bigemap APP 详细使用教程,入门学习PPT
  • element table 表格多选框选中高亮
  • KubeBlocks for ClickHouse 容器化之路
  • 【运维进阶】shell三剑客
  • DeepSeek大模型如何重塑AI Agent?从技术突破到行业落地
  • 环境搭建-dockerfile构建镜像时apt软件包出现exit100错误+ pip下载python库时下载过慢的解决方法
  • SpringWeb详解
  • CorrectNav——基于VLM构建带“自我纠正飞轮”的VLN:通过「视觉输入和语言指令」预测导航动作,且从动作和感知层面生成自我修正数据
  • 【LeetCode热题100道笔记+动画】三数之和
  • Linux上安装MySQL 二进制包
  • TENON AI-AI大模型模拟面试官
  • idea进阶技能掌握, 自带HTTP测试工具HTTP client使用方法详解,完全可替代PostMan
  • 【力扣 买卖股票的最佳时机 Java/Python】
  • 数据库架构开发知识库体系
  • VGG改进(3):基于Cross Attention的VGG16增强方案
  • Foundry与Uniswap V2实战开发指南
  • 【自记】Power BI 中 DISTINCT 和 ALLNOBLANKROW 的区别说明
  • 比特分割 + 尖峰保留:FlashCommunication V2 实现任意比特通信与 3.2× 加速
  • 一键授权登录
  • Windows暂停更新10年最简单的设置