问题描述
给定一个字符串 s,找出其中不含有重复字符的 最长子串 的长度。例如:
-
输入
s = "abcabcbb",输出3(最长子串 "abc") -
输入
s = "bbbbb",输出1(最长子串 "b") -
输入
s = "pwwkew",输出3(最长子串 "wke" 或 "kew")
什么是滑动窗口算法?
滑动窗口是一种高效解决数组/字符串子区间问题的算法。它通过维护一个可动态扩展/收缩的窗口(由左右指针定义),在遍历过程中更新窗口状态并记录最优解。该算法通常能在 O(n) 时间复杂度内解决问题。
算法思路
-
初始化:左指针
left = 0,最大长度maxLen = 0 -
遍历字符串(右指针
right从 0 开始):-
若当前字符未出现在窗口中:
-
将其加入窗口
-
更新最大长度:
maxLen = max(maxLen, 窗口长度) -
右指针右移
-
-
若当前字符已在窗口中:
-
移除左指针指向的字符
-
左指针右移
-
-
-
返回结果:
maxLen
优化:快速跳转左指针
在基础实现中,左指针每次只移动一步。我们可以通过 记录字符最后出现的位置 来优化:
-
使用
Map<Character, Integer>存储字符及其最后出现的位置 -
当遇到重复字符时,直接跳转左指针:
left = max(left, 重复字符最后位置 + 1)
代码实现(两种方法)
方法1:基础实现(逐步移动左指针)
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int left = 0, right = 0, maxLen = 0;
while (right < s.length()) {
char c = s.charAt(right);
if (!set.contains(c)) {
set.add(c);
maxLen = Math.max(maxLen, right - left + 1);
right++;
} else {
set.remove(s.charAt(left));
left++;
}
}
return maxLen;
}
}
方法2:优化实现(跳转左指针)
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int maxLen = 0, left = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (map.containsKey(c)) {
left = Math.max(left, map.get(c) + 1);
}
map.put(c, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
}
算法分析
| 方法 | 时间复杂度 | 空间复杂度 | 优势 |
|---|---|---|---|
| 基础实现 | O(n) | O(1) | 逻辑简单,易于理解 |
| 优化实现 | O(n) | O(1) | 减少左指针移动次数,效率更高 |
说明:空间复杂度 O(1) 是因为字符集大小固定(ASCII 字符最多 128 个)
关键点总结
-
滑动窗口核心思想:
-
右指针:探索新字符,扩展窗口
-
左指针:移除重复字符,收缩窗口
-
-
优化核心:
-
通过记录字符最后位置实现左指针跳转
-
避免不必要的逐步移动,提升效率
-
-
终止条件:右指针到达字符串末尾
-
结果更新:每次扩展窗口后更新最大长度
适用问题场景
滑动窗口算法特别适合解决以下类型问题:
-
子串/子数组的最值问题(如本题)
-
满足特定条件的连续子序列问题
-
需要高效遍历数组/字符串的场景
通过掌握滑动窗口算法,可以高效解决一大类字符串和数组相关的
931

被折叠的 条评论
为什么被折叠?



