双指针与滑动窗口中等1 种解法
#3无重复字符的最长子串
返回字符串中不含重复字符的最长连续子串长度。
#字符串#哈希表#滑动窗口
原题
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。
提示:
0 <= s.length <= 105s由英文字母、数字、符号和空格组成
解题主线
- 窗口内始终保持每个字符至多出现一次;右端加入重复字符后,左端持续收缩直到约束恢复。
- 字符最后出现位置可以让左边界一步跳过冲突位置,避免逐个递减频次。
解法 1:滑动窗口计数
右指针扩张并记录字符频次;当前字符重复时移动左指针并减少沿途频次。
-
时间复杂度: O(n)
-
空间复杂度: O(k),k 为窗口内不同 UTF-16 字符数
import java.util.HashMap;
import java.util.Map;
final class Solution {
public int lengthOfLongestSubstring(String s) {
// 窗口不变量:收缩结束后,每个字符至多出现一次。
// 右端字符重复时只需移出到其频次恢复为 1,随后才能更新答案。
Map<Character, Integer> frequency = new HashMap<>();
int left = 0;
int answer = 0;
for (int right = 0; right < s.length(); right++) {
char added = s.charAt(right);
frequency.merge(added, 1, Integer::sum);
while (frequency.get(added) > 1) {
char removed = s.charAt(left++);
frequency.put(removed, frequency.get(removed) - 1);
}
answer = Math.max(answer, right - left + 1);
}
return answer;
}
}实现提示
- 合并旧文件中重复的两个频次窗口实现。
边界与易错点
- 子串必须连续,不能按子序列处理。
- 旧文件两个方法完全等价,因此按同一滑动窗口解法去重。
- Java 的 char 表示 UTF-16 代码单元;若题意扩展到完整 Unicode 码点,应改用 codePoints。
整理来源
由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。
medium/Q003_LongestSubstring.java