算法 - 无重复字符的最长子串(03)

原理:

定义左右两个指针,保证两个指针对应的子串中没有重复的字符,寻找并记录最长的子串长度。如果窗口满足条件,右指针向右滑动扩大窗口,更新最优值;如果窗口不满足条件,左指针向右缩小窗口。 (这里需要借助数据结构 -- 哈希集合,来判断是否有重复字符)

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目:

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

示例:

输入:nums= [-1,0,3,5,9,12],target=9
输出: 4

 

提示: 

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

 

思路1:滑动窗口

将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串,我们记录下这个子串的长度。在枚举结束后,我们找到的最长的子串的长度即为答案。

在上面的流程中,我们还需要使用一种数据结构来判断是否有重复的字符,常用的数据结构为哈希集合(即 C++ 中的 std::unordered_set,Java 中的 HashSet,Python 中的 set, JavaScript 中的 Set)。

在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。

算法 - 无重复字符的最长子串(03)_第1张图片

答案:

① 方法1:暴力求解

逐个生成子字符串,看它是否不含有重复的字符 。

算法 - 无重复字符的最长子串(03)_第2张图片

该方法虽然可以实现,但是不够优雅。 【208 ms         47.31 MB】

      var lengthOfLongestSubstring = function (s) {
        // 左右指针
        let left = (right = max = 0);
        let val = (endVal = "");
        let length = s.length;

        while (right < length) {
          endVal = s.charAt(right);
          if (val.indexOf(endVal) < 0) {
            val = val + endVal;
            // 右指针向右移动
            right++;
            if (right >= length) {
              max = max > val.length ? max : val.length;
            }
          } else {
            max = max > val.length ? max : val.length;
            left++;
            right = left + 1;
            val = s.charAt(left);
          }
        }
        return max;
      };
      console.log("答案", lengthOfLongestSubstring("abcabcbb")); // 3
      console.log("答案", lengthOfLongestSubstring("")); // 0
      console.log("答案", lengthOfLongestSubstring(" ")); // 1
      console.log("答案", lengthOfLongestSubstring("  ")); // 1
      console.log("答案", lengthOfLongestSubstring("aa")); // 1
      console.log("答案", lengthOfLongestSubstring("ab")); // 2
      console.log("答案", lengthOfLongestSubstring("pwwkew")); // 3

② 方法二:滑动窗口及优化

关键字:重复字符 --> 出现1次

模式识别1:一旦涉及出现次数,就需要用到散列表

构造子串,散列表存下标;
模式识别2:涉及子串,考虑滑动窗口 

算法 - 无重复字符的最长子串(03)_第3张图片

 答案1:官方题解【96 ms      45.49 MB】

      var lengthOfLongestSubstring = function (s) {
        // 哈希集合,记录每个字符是否出现过
        const occ = new Set();
        const length = s.length;
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        let start = -1,
          res = 0;
        for (let i = 0; i < length; ++i) {
          if (i != 0) {
            // 左指针向右移动一格,移除一个字符
            occ.delete(s.charAt(i - 1));
          }
          while (start + 1 < length && !occ.has(s.charAt(start + 1))) {
            // 不断地移动右指针
            occ.add(s.charAt(start + 1));
            ++start;
          }
          // 第 i 到 start 个字符是一个极长的无重复字符子串
          res = Math.max(res, start - i + 1);
        }
        return res;
      };
      console.log("答案", lengthOfLongestSubstring("abcabcbb")); // 3
      console.log("答案", lengthOfLongestSubstring("")); // 0
      console.log("答案", lengthOfLongestSubstring(" ")); // 1
      console.log("答案", lengthOfLongestSubstring("  ")); // 1
      console.log("答案", lengthOfLongestSubstring("aa")); // 1
      console.log("答案", lengthOfLongestSubstring("ab")); // 2
      console.log("答案", lengthOfLongestSubstring("pwwkew")); // 3

答案2:个人【88 ms      45.73 MB】

      var lengthOfLongestSubstring = function (s) {
        // 定义左指针、右指针、长度、最大长度
        let left = (right = length = maxLength = 0)
        // 定义set集合
        let set = new Set()
        // 遍历字符串
        while (right < s.length) {
          if (!set.has(s[right])) {
            // 不存在相同字符,把当前字符添加到字符集中
            set.add(s[right])
            // 更新长度
            length++
            if (length > maxLength) {
              maxLength = length
            }
            // 右指针往右移动
            right++
          } else {
            // 删除字符集中的第一个字符
            set.delete(s[left])
            // 更新长度
            length--
            // 左指针往右移动
            left++
          }
        }
        return maxLength
      };

答案3:视频【68 ms       45.91 MB】

      var lengthOfLongestSubstring = function (s) {
        // 定义左指针、右指针、长度、最大长度
        let left = (right = length = maxLength = 0)
        // 定义set集合
        let set = new Set()
        // 遍历字符串
        while (right < s.length) {
          if (!set.has(s[right])) {
            // 不存在相同字符,把当前字符添加到字符集中
            set.add(s[right])
            // 更新长度
            length++
            if (length > maxLength) {
              maxLength = length
            }
            // 右指针往右移动
            right++
          } else {
            while (set.has(s[right])) {
              // 删除字符集中的第一个字符
              set.delete(s[left])
              // 更新长度
              length--
              // 左指针往右移动
              left++
            }
            set.add(s[right])
            length++
            right++
          }
        }
        return maxLength
      };

视频:

一道算法题理解滑动窗思想!【趣刷Leetcode】 No.3 无重复字符的最长子串_哔哩哔哩_bilibili

你可能感兴趣的:(算法,算法)