无重复字符的最长子串

124 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

今天八夕:咕呱,咕呱,咕呱。🐸

题目:无重复字符的最长子串

前言:在刚看到这道题的时候感觉做过,感觉和滑动窗口有关,于是去历史做题记录里翻找,果真,三个月前通过一次。但是这次再看只能想出个大概流程,关于细节问题全部忘光光了。---------------------------------------复习的重要性

image.png

题目示例:

示例 1:

输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2:

输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3:

输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。   请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

起初没有认真读题,以为就是无重复的字符串有几个,于是用了set直接输出。但是示例三却输出4。反过来认真读题后才知道,是 子 串

思路分析:方法1

三个月前做的那次用的就是滑动窗口,所以这次还是先说滑动窗口。

先定义set用来记录每一个字符,方便后续判断是否有重复的字符出现。

用两个指针来形成一个可滑动的窗口,两个指针的范围就是最长子串的范围。

如何确定两个指针的范围:

  1. 左指针与右指针起初都指向第一位字符。
  2. 右指针先向右移动,每指向一位就存进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。