【LeetCode】3. 无重复字符的最长子串
题目
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入:s = "abcabcbb"
输出:3
解释:因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入:s = "bbbbb"
输出:1
解释:因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入:s = "pwwkew"
输出:3
解释:因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
解题思路
核心要点:
- 子串是连续的字符序列,不同于子序列
- 需要找到不包含重复字符的最长子串长度
- 高效判断字符是否重复是关键
最有效的解法是滑动窗口法 结合哈希集合:
- 使用左右两个指针表示当前窗口的边界
- 右指针不断向右移动,将字符加入哈希集合
- 当遇到重复字符时,移动左指针并从集合中移除相应字符,直到窗口中没有重复字符
- 每次移动后计算当前窗口长度,并更新最大长度
这种方法只需一次遍历,时间复杂度为O(n),非常高效。
代码实现
java
import java.util.HashSet;
import java.util.Set;
public class LongestSubstring {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,用于存储当前窗口中的字符
Set<Character> set = new HashSet<>();
int n = s.length();
// 右指针,初始值为 -1,相当于在字符串的左边界的左侧,还没有开始移动
int right = -1;
int maxLength = 0;
for (int left = 0; left < n; left++) {
if (left != 0) {
// 左指针向右移动一格,移除一个字符
set.remove(s.charAt(left - 1));
}
// 不断移动右指针,直到遇到重复字符
while (right + 1 < n && !set.contains(s.charAt(right + 1))) {
set.add(s.charAt(right + 1));
right++;
}
// 更新最大长度
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
// 测试示例
public static void main(String[] args) {
LongestSubstring solution = new LongestSubstring();
System.out.println(solution.lengthOfLongestSubstring("abcabcbb")); // 输出:3
System.out.println(solution.lengthOfLongestSubstring("bbbbb")); // 输出:1
System.out.println(solution.lengthOfLongestSubstring("pwwkew")); // 输出:3
System.out.println(solution.lengthOfLongestSubstring("")); // 输出:0
}
}
复杂度分析
- 时间复杂度:O(n),其中n是字符串的长度。左指针和右指针分别最多移动n次。
- 空间复杂度:O(min(m, n)),其中m是字符集的大小。在最坏情况下,整个字符串都没有重复字符,此时集合的大小为n。
关键要点
- 滑动窗口技术的应用:通过调整左右指针维护一个有效的窗口
- 哈希集合的使用:快速判断字符是否已存在于当前窗口中
- 一次遍历即可完成计算,效率高,适合处理大规模字符串(题目中提示长度可达5×10^4)
- 边界处理:当字符串为空时,直接返回0