持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
每日刷题 2022.10.26
- leetcode原题链接:leetcode.cn/problems/lo…
- 难度:中等
题目
- 给定一个字符串
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
由英文字母、数字、符号和空格组成
解题思路
- 根据题目的含义:需要找到一个最长的且不重复的连续子串。
- 此时想到:既然是需要找到一个连续的子串,那么就可以将其当作一个区间来看,那么这个符合要求的区间长度中取最大值就可以了。
- 那么如何找到这样的区间呢?其实直接双层
for
循环就可以实现,但是这样会浪费掉很多已经可以利用起来的子区间,例如:对于字符串acd
而言:ac
是满足要求的区间,加上d
其也是满足要求的区间,那么第一次遍历到满足的区间ac
后,就只需要再后面再添加上d
就可以了,无需从头再次遍历a
然后ac
,最后acd
。 - 故:就想到了使用滑动窗口(双指针的解法),配合使用
set
集合。使用set
集合记录区间内出现过的字母,然后使用滑动窗口根据set
中统计的数据来进行滑动。 - 具体的:当右指针
r
指向的当前值cur
是已经出现在set
集合中,那么就移动左指针l
(每移动一次l
指针,就表示从区间内删除一个值,相应的也需要将其从set
集合中删除),当移动左指针l
到set
集合中不包含当前的字符为止(!此时的l
和r
之间包含的区间一定是满足的!!)。此时再次移动r
,循环上述步骤。
注意事项
- 因为题目中要求找到最长的,因此我们通常会想到:当第一次找到不满足的时候,再统计区间的长度,那一定是最大的,但是这样就会漏掉一种情况:如果到结尾(数组的末尾),都是符合要求的(即:没有碰到不符合要求的),这样的话不就漏掉了。
- 解决方法:不管是不是最长的,只要满足就要取
max
,这样就可以将每一种符合要求的区间长度都记录下来。
拓展
- 滑动窗口:若当前区间内满足要求,则将右指针
r
一直往后移,当遇到第一个不满足的时候,停止移动r
, 接着移动左指针l
,直到区间内再次满足要求。 - 那么请问:当数组中存在负值的时候,还能使用滑动窗口来处理区间和的大小的问题吗?答案:不能。因为存在负值的情况下,就不能满足右指针
r
向后移动,一定是大于等于某个值的,同理也不能保证左指针l
向前移动,一定是小于等于某个值的。 - 那么不能使用滑动窗口如何解决这样的问题呢?就可以考虑下前缀和是否适用。
AC
代码
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 使用set集合+双指针解决
let set = new Set(), l = 0, r = 1, n = s.length, maxx = 1;
if(n === 0) return 0;
set.add(s[l]);
while(r < n) {
let cur = s[r];
while(set.has(cur) && r < n) {
let t = s[l];
set.delete(t);
l++;
}
r++;
set.add(cur);
maxx = Math.max(maxx, r - l);
}
return maxx;
};