算法:无重复字符的最长子串

199 阅读3分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

滑动窗口类问题特点

  • 计算过程与两端点表示的区间相关的称为滑动窗口。
  • 默认固定大小的窗口,在一些条件触发的情况下,可能会将其大小进行修改。
  • 滑动窗口本身并不是解决问题的一种方法(或者说算法),它其实就是问题本身。
  • 滑动窗口一定是同向移动的。
  • 滑动窗口是一类问题,不同的问题需要使用不同的算法和数据结构来解决。

题目

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

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

类型:滑动窗口类问题。

示例1:

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

示例2:

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

示例3:

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

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/lo… 

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解题思路

  • 定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为前子串中下标对应的出现频率
  • left,right 为窗口的左右边界
  • 循环时,只要左边界小于总字符串长度(left < n),就可以一直向右滑
  • 初始化的时候,滑动窗口的大小为0,(left=0)。因为窗口的右边界,要从字符串的第一位开始,所以初始化时(right = -1)
  • 每次窗口滑动时只要不遇到重复的字符,窗口右边界+1,将改字符记录到map中
  • 如果遇到重复的值了,也就是我们定义的map中已经纯在了(!!freqMap[nextLetter]true 时),窗口向右移动一位,原先位置的字符出现频率设置为0。
  • 取字串的长度

代码实现

/**
 * @param {string} s
 * @return {number}
 */
let lengthOfLongestSubstring = function (str) {
  let n = str.length
  // 滑动窗口为s[left...right]
  let left = 0
  let right = -1
  let freqMap = {} // 记录当前子串中下标对应的出现频率
  let max = 0 // 找到的满足条件子串的最长长度

  while (left < n) {
    let nextLetter = str[right + 1]
    if (!freqMap[nextLetter] && nextLetter !== undefined) {
      freqMap[nextLetter] = 1
      right++
    } else {
      freqMap[str[left]] = 0
      left++
    }
    max = Math.max(max, right - left + 1)
  }

  return max
}

示例分析

image.png

max 的变化

// 第 1 次循环 
left = 0
right = 0
max = 1
map = {
    p:1
}

//2
left = 0
right = 1
max = 2
map = {
    p:1
    w:1
}


//3
left = 1
right = 1
max = Math.max(2,1)
map = {
    p:0
    w:1
}


// 4
left = 2
right = 1
max = Math.max(2,0)
map = {
    p:0
    w:0
}

//5
left = 2
right = 1
max = Math.max(2,0)
map = {
    p:0
    w:0
}

//5 w
left = 2
right = 2
max = Math.max(2,1)
map = {
    p:0
    w:1
}

//6 k
left = 2
right = 3
max = Math.max(2,2)
map = {
    p:0
    w:1
    k:1
}

//7 e
left = 2
right = 4
max = Math.max(2,3)
map = {
    p:0
    w:1
    k:1
    e:1
}

//8 w
left = 3
right = 4
max = Math.max(3,2)
map = {
    p:0
    w:0
    k:1
    e:1
}

//9 undefined
left = 4
right = 5
max = Math.max(3,2)
map = {
    p:0
    w:1
    k:0
    e:1
}

//10 undefined
left = 5
right = 5
max = Math.max(3,1)
map = {
    p:0
    w:1
    k:0
    e:0
}