【LeetCode】无重复字符的最长子串(中等) - Set - 滑动窗口 - 收缩窗口

1,606 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

今天我们来看一道字符串相关的题,这里用JavaScript给出三种解法,其中第三种是我想到的最通俗易懂的解法,我也给出了详细的解析过程,可以看看~

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

力扣地址

【解法一】暴力遍历 + Set

最快想到的就是暴力遍历,以每个字符串为开头遍历一遍 也就是总共要遍历两遍

function lengthOfLongestSubstring(s) {
  let len = s.length;
  let result = 0;

  for (let i = 0; i < len; i++) {
    let set = new Set();
    let maxLen = 0;
    // 从i的位置遍历得到最长子串的长度
    let j = i;
    while (j < len && !set.has(s[j])) {
      set.add(s[j]);
      maxLen++;
      j++;
    }
    // 取历史最大值
    result = Math.max(result, maxLen);
  }
  return result;
}

在这里插入图片描述

【解法二】滑动窗口

这是官方题解的答案,其实不是特别清晰,详细细节看注释

function lengthOfLongestSubstring(s) {
  let len = s.length;
  let result = 0;
  let set = new Set();
  // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
  let j = -1;
  
  for (let i = 0; i < len; i++) {
    if (i !== 0) {
      // 左指针向右移动一格,移除一个字符
      set.delete(s[i - 1]);
    }
    while (j + 1 < len && !set.has(s[j + 1])) {
      // 不断地移动右指针
      set.add(s[j + 1]);
      j++;
    }
    // 第 i 到 j 个字符是一个极长的无重复字符子串
    result = Math.max(result, j - i + 1);
  }
  return result;
}

在这里插入图片描述

【解法三】双指针 - 滑动(收缩)窗口(这种写法通俗易懂)

其实可以通过观察可以优化,我们制作一个窗口,让窗口中的字符串满足题目要求(无重复) 怎么让他满足要求呢? 那就要滑动窗口了,循环去掉左边第一个元素,直到窗口中元素无重复,此时再扩大窗口

滑动窗口有两个关键点:扩张 + 收缩

  • 首先(右指针)扩张到滑动窗口不满足条件的时候暂停,

  • (左指针)开始收缩窗口,让窗口满足条件后再进行扩张(右指针)

function lengthOfLongestSubstring(s) {
  let len = s.length;
  let result = 0;

  let set = new Set();
  // 左指针用来收缩窗口
  let left = 0;
  // 右指针用来扩张窗口
  let right = 0;

  while (left < len) {
    // 如果不重复,就不断扩张窗口,元素添加到set中
    while (right < len && !set.has(s[right])) {
      set.add(s[right]);
      right++;
    }
    // 到这里说明有元素重复了,先记录子串长度,然后收缩窗口
    result = Math.max(result, right - left);
    // 收缩窗口
    set.delete(s[left]);
    left++;
  }
  return result;
}

在这里插入图片描述