Zhupi222
首页关于我项目博客联系我

LeetCode 567 解题优化记录:从暴力到滑动窗口

2025-06-20约5分钟
记录我在解决 LeetCode 567(Permutation in String)时从暴力解法优化为滑动窗口的过程,并总结性能改进思路。
LeetCode滑动窗口字符串优化记录

💡 题目简介

LeetCode 567:判断字符串 s2 中是否包含 s1 的排列。

  • 输入:s1 = "ab", s2 = "eidbaooo"
  • 输出:true(因为 "ba" 是 s1 的一个排列,出现在了 s2 中)

🚧 初始暴力解法

第一版思路是暴力枚举 s2 中所有长度为 s1.length 的子串,然后和 s1 的频率 Map 逐一对比。

function getCharFrequencyMap(s) { const map = new Map() for (const ch of s) { map.set(ch, (map.get(ch) || 0) + 1) } return map } function isSameFrequencyMap(map1, map2) { if (map1.size !== map2.size) return false for (const [key, val] of map1.entries()) { if (map2.get(key) !== val) return false } return true } var checkInclusion = function(s1, s2) { const map = getCharFrequencyMap(s1) let left = 0, right = s1.length - 1 while (right < s2.length) { const tmpMap = getCharFrequencyMap(s2.slice(left, right + 1)) if (isSameFrequencyMap(map, tmpMap)) return true left++ right++ } return false }
  • ✅ 能通过样例
  • ❌ 子串每次都重新建 Map,性能瓶颈在于重复统计

✅ 优化:滑动窗口 + Map

借助滑动窗口的思想,用一个 Map window 记录当前窗口内字符频次,并维护一个 valid 变量,表示窗口内频次完全匹配的字符数。

var checkInclusion = function(s1, s2) { const map = getCharFrequencyMap(s1) const window = new Map() let left = 0, right = 0, valid = 0 while (right < s2.length) { const ch = s2[right] right++ if (map.has(ch)) { window.set(ch, (window.get(ch) || 0) + 1) if (window.get(ch) === map.get(ch)) { valid++ } } while (right - left >= s1.length) { if (valid === map.size) return true const del = s2[left] left++ if (map.has(del)) { if (window.get(del) === map.get(del)) { valid-- } window.set(del, window.get(del) - 1) } } } return false }

🔍 对比优化点:

比较项暴力解滑动窗口
子串频次统计每次重新统计滑动更新,O(1) 复杂度
总体时间复杂度O(n × m)O(n)
实际表现小样本可过大样本稳定高效

🧠 总结

  • 对于子串匹配问题,滑动窗口 + 频次统计 + 有效计数 是性能利器。
  • 一定要养成“避免重复计算”的优化思维。
  • 本题使用 Map 实现频次统计,对所有字符集都适用,也可以替换为定长数组优化为 O(26)。

如果你也在刷题中遇到性能问题,不妨静下心来思考是否可以引入状态缓存或滑动窗口技巧 😉。

上一篇家庭食谱小程序开发总结(基于 Taro + Redux Toolkit)下一篇LeetCode 213 环形抢劫:二维 DP 同步处理两种区间

相关文章

LeetCode 213 环形抢劫:二维 DP 同步处理两种区间
2025-06-20约6分钟
动态规划LeetCode算法