LeetCode 3. 无重复字符的最长子串解析
LeetCode 3. 无重复字符的最长子串解析
- 1. 题目链接
- 2. 问题分析
- 3. 方法一:暴力法(不推荐)
- 复杂度分析:
- 4. 方法二:滑动窗口(推荐)
- 复杂度分析:
- 5. 方法三:优化的滑动窗口
- 复杂度分析:
- 6. 算法对比
- 7. 关键点与技巧
- 8. 总结
1. 题目链接
LeetCode 3. 无重复字符的最长子串解析
2. 问题分析
解决这个问题,我们需要找到一个字符串中不包含重复字符的最长子串的长度。关键在于处理字符串时,当遇到重复字符,如何高效地调整子串的起始位置。
3. 方法一:暴力法(不推荐)
最直观的方法是检查所有可能的子串,判断它们是否包含重复字符。
public int lengthOfLongestSubstringBruteForce(String s) {int n = s.length();int maxLength = 0;for (int i = 0; i < n; i++) {for (int j = i; j < n; j++) {if (isUnique(s, i, j)) {maxLength = Math.max(maxLength, j - i + 1);}}}return maxLength;
}private boolean isUnique(String s, int start, int end) {Set<Character> set = new HashSet<>();for (int i = start; i <= end; i++) {char c = s.charAt(i);if (set.contains(c)) {return false;}set.add(c);}return true;
}
复杂度分析:
时间复杂度:O(n³) - 三层嵌套循环(两层for循环,一层哈希集查找)
空间复杂度:O(min(n, m)) - 哈希集的大小
这种方法在LeetCode上会超时,不适合处理较长的字符串。
4. 方法二:滑动窗口(推荐)
滑动窗口算法可以有效地将时间复杂度降低到O(n)。基本思想是维护一个窗口,当遇到重复字符时,移动左边界。
class Solution {public int lengthOfLongestSubstring(String s) {int n = s.length();int l = 0;int r = 0;Set<Character> set = new HashSet<>();int res = 0;while (r < n) {if (set.contains(s.charAt(r))) {set.remove(s.charAt(l));l++;} else {set.add(s.charAt(r));r++;}res = Math.max(res, set.size());}return res;}
}
复杂度分析:
时间复杂度:O(n) - 每个字符最多被访问两次(左指针和右指针各一次)
空间复杂度:O(min(n, m)) - 哈希集的大小,其中m是字符集大小
5. 方法三:优化的滑动窗口
我们可以进一步优化,使用HashMap存储字符及其索引,这样当遇到重复字符时,可以直接跳转到正确的位置。
class Solution {public int lengthOfLongestSubstring(String s) {int n = s.length();int l = 0;int r = 0;Map<Character, Integer> map = new HashMap<>();int res = 0;while (r < n) {if (map.containsKey(s.charAt(r)) && l <= map.get(s.charAt(r))) {l = map.get(s.charAt(r)) + 1;}map.put(s.charAt(r), r);res = Math.max(res, r - l + 1);r++;}return res;}
}
复杂度分析:
时间复杂度:O(n) - 只需要遍历一次字符串
空间复杂度:O(min(n, m)) - HashMap的大小
6. 算法对比

7. 关键点与技巧
滑动窗口原理:维护一个不包含重复字符的窗口,动态调整窗口的左右边界。
哈希集合的使用:利用HashSet的O(1)查找时间来判断字符是否重复。
哈希映射的优化:通过存储字符索引,实现左指针的直接跳转,避免逐步移动。
边界条件处理:
-
空字符串输入
-
全相同字符的字符串
-
单个字符的字符串
复杂度权衡:在时间复杂度和空间复杂度之间做出合理选择,根据实际场景选择最合适的算法。
注意!!!
- 在
r++之前 计算窗口长度r - l + 1 - 滑动窗口边界,需要
l <= map.get(s.charAt(r))而不是<
8. 总结
解决"无重复字符的最长子串"问题的核心在于掌握滑动窗口技术。通过维护一个动态的窗口,我们可以在O(n)时间内高效解决问题。优化的关键在于利用合适的数据结构(HashSet或HashMap)来快速判断字符重复性,从而及时调整窗口大小。
这种滑动窗口的技巧不仅可以解决此题,还可以应用于很多其他子串、子数组问题中,是算法学习中一个非常重要的模式。
