携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
今天八夕:咕呱,咕呱,咕呱。🐸
题目:无重复字符的最长子串
前言:在刚看到这道题的时候感觉做过,感觉和滑动窗口有关,于是去历史做题记录里翻找,果真,三个月前通过一次。但是这次再看只能想出个大概流程,关于细节问题全部忘光光了。---------------------------------------复习的重要性。
题目示例:
示例 1:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
起初没有认真读题,以为就是无重复的字符串有几个,于是用了set直接输出。但是示例三却输出4。反过来认真读题后才知道,是 子 串。
思路分析:方法1
三个月前做的那次用的就是滑动窗口,所以这次还是先说滑动窗口。
先定义set用来记录每一个字符,方便后续判断是否有重复的字符出现。
用两个指针来形成一个可滑动的窗口,两个指针的范围就是最长子串的范围。
如何确定两个指针的范围:
- 左指针与右指针起初都指向第一位字符。
- 右指针先向右移动,每指向一位就存进set中一位。当遇到与set中重复的字符时,就说明,从左指针到此右指针之间无重复字符的数字范围已经达到最大。此时 记录下该最大值(右指针-左指针),同时收缩窗口(左指针向右移)。
代码实现:
var lengthOfLongestSubstring = function (s) {
var set = new Set() //左右指针用来收缩窗口
var left = 0, right = 0, result = 0;
while (left < s.length) {
while (right < s.length && !set.has(s[right])) {
//扩张窗口
set.add(s[right])
right++;
}
//碰到重复的,先记录长度,再收缩窗口
result = Math.max(result, right - left)
set.delete(s[left])
left++;
}
return result;
};
逻辑:扩张窗口收缩窗口。👆
思路分析:方法2
相同:两个指针,放置位置与上个思路相同。右指针向右移动 同时把字符存进set数组中,左指针指向第一个位置先不动。
不同:
当右指针碰见了set中已存在过的字符时,说明从左指针到此右指针之间的 无重复字符 以达到最大长度。通透点说就是 碰见重复字符之后,前一段左右指针之间的范围就已经达到最大了,记录该范围的子串数目后,就可以和后面的操作可以无关了。
于是把左指针指向该右指针的下一位,同时右指针向右移动一位,开始新的寻找旅途。
每遇到一次重复字符,就这样做:更新左右指针之间字符数目以寻找最大值,左右指针来到新的位置。
代码实现:
var lengthOfLongestSubstring = function (s) {
let l = 0; // 定义左指针
let res = 0; // 结果
let map = new Map(); // 存放字符和对应下标
for (let r = 0; r < s.length; r++) {
// 如果出现了重复字符,则把左指针移到重复字符的下一位。
注意同时满足右指针指向的重复字符的索引要大于左指针。
if (map.has(s[r]) && map.get(s[r]) >= l) {
l = map.get(s[r]) + 1;
}
res = Math.max(res, r - l + 1); // 计算结果
map.set(s[r], r); // 存下每个字符的下标
}
return res;
};
其实这俩方法好像差不多,但我又说不清楚是哪里差不多,就这样吧,当我是凑字数好了。
这个饮食不习惯,吃饭尝不出咸淡味道的日子竟然到头了。泪目。
还是要应景说句祝你们七夕快乐。咕呱咕呱。
熊 体 锅 9。