无重复字符的最长子串
Longest Substring Without Repeating Characters
在字符串中找一段连续、没有重复字符的子串,返回它的最大长度。
在力扣中练习题目描述
给定一个字符串 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由英文字母、数字、符号和空格组成
解题思路
主解法
滑动窗口 · 最近出现位置
右端扩张遇到重复,左端只向前跳
暴力枚举每段子串会重复检查很多字符。换个角度:固定右端点,维护以它结尾的最长无重复窗口。
用自拟输入 abcbadefbg 推演。维护 last[ch] 记录字符上次出现的位置,[left, right] 表示当前窗口。读到下标 3 的 b 时,上次位置 1 仍在窗口里,必须让 left 跳到 2,越过旧 b。
分步图解
读到 "b",上次位置为 1。
- left
- 0
- right
- 3
- previous
- 1
- best
- 3
关键是出现过不等于当前窗口内重复。读到下标 4 的 a 时,上次位置为 0,已经小于 left = 2,直接忽略;如果无条件写 left = last[ch] + 1,左边界就会倒退,把重复字符重新放进窗口。
分步图解
读到 "a",上次位置为 0。
- left
- 2
- right
- 4
- previous
- 0
- best
- 3
继续向后,窗口先扩张出 cbadef,长度为 6;下标 8 的 b 再次触发收缩。每轮恢复合法后再更新 best,最终答案为 6。左右边界都只向右走,所以整体时间为 O(n)。
时间 O(n)空间 O(1)(固定字符表)
易错点
- left 只能向右移动;窗口外的历史位置不能让它倒退。
- 先查询旧 last,再登记当前下标,避免与自己产生冲突。
- 题目要求连续子串,不能把不相邻的字符拼在一起。
代码与演示
演示输入
s = "abcbadefbg"窗口初始为空,所有字符的上次出现位置视为 -1。
- left
- 0
- right
- -1
- previous
- -1
- best
- 0
查看 C++ 代码
延伸阅读
- 滑动窗口的顺序是扩张、恢复约束、更新答案。
- 这里用固定 256 大小的字符表,并将 char 转成 unsigned char 后作为索引。