给定一个字符串 s,请你找出其中不含有重复字符的最长子串的长度。
示例:
本题要求的是“不含重复字符的最长子串”。
子串要求字符必须连续,题目又要求在连续区间中维护“不重复”这个条件,所以可以使用滑动窗口。
窗口右边界不断向右扩展,表示尝试加入新的字符;如果加入新字符后出现重复,就移动左边界,让窗口重新变成无重复字符的合法窗口。
使用闭区间窗口 [left, right]。
left:当前无重复字符窗口的左边界。right:当前无重复字符窗口的右边界,也是正在遍历的新字符位置。lastIndexMap:记录每个字符最近一次出现的下标。ans:记录当前找到的最长无重复子串长度。例如遍历到 right 时,窗口 [left, right] 表示当前正在检查的一段连续子串。
每次 right 向右移动一位,把 s[right] 作为新字符加入窗口。
如果 s[right] 之前出现过,设它上一次出现的位置是 lastIndex:
lastIndex >= left,说明重复字符还在当前窗口内,需要把 left 移动到 lastIndex + 1。lastIndex < left,说明旧字符已经不在当前窗口内,不需要移动 left。这两个情况可以统一写成:
处理完当前字符后,再把 s[right] 的最近出现位置更新为 right。
本题求最长合法窗口。
每次处理完重复字符后,窗口 [left, right] 一定是不含重复字符的合法窗口,所以此时用窗口长度更新答案:
注意:答案要在窗口重新合法后更新,不能在发现重复但还没调整 left 时更新。
边界条件:
s 是空字符串,循环不会执行,最终返回初始值 0。s 只有一个字符,窗口长度最大为 1。left 不移动,答案就是 s.length。1。易错点:
不要直接写成:
因为重复字符上一次出现的位置可能已经在窗口左侧,直接赋值会导致 left 回退。
例如 s = "abba":
b 时,窗口左边界移动到下标 2。a 时,旧的 a 在下标 0,已经不在当前窗口内。left 改成 1,窗口会错误变成 "bba"。所以 left 必须只能向右移动,不能回退。
O(n),其中 n 是字符串长度。right 从左到右遍历一次,left 也只会向右移动,不会回退。O(min(n, m)),其中 m 是字符集大小。lastIndexMap 最多记录出现过的字符最近下标。