滑动窗口算法

  • 框架
  • 实例
  • 76.最小覆盖子串
  • 567.字符串的排列
  • 438.找到字符串中所有字母的异位词
  • 3.无重复字符的最长子串


框架

slideWindow (s, t) {
 	// 定义需要个数的对象、当前窗口对象
    const need = {}
    const window = {}
 	// 定义左右索引
    let left = 0
    let right = 0
 	// valid种类数
    let valid = 0
 	// 找最小子串
    let start = 0
    let len = Infinity
 	// 结果数组: 多个异位词、最长子串长度
    let res = []
    
    <!--需要对象赋值-->
    for (const i of t) {
        need[i] = (need[i] || 0) + 1
    }
    
    <!--遍历子串s-->
    while (right < s.length) {
        <!--右索引移动-->
        const r = s[right]
        right++
        
        <!--更新窗口:-->
        // 当前字符串为需要,窗口对象属性值+1,加1后若当前属性个数与需要个数相等,种类数valid++
        // 无重复子串窗口更新只需: 窗口数window[r]+1
        if (need[r]) {
          window[r] = (window[r] || 0) + 1
          if (window[r] === need[r]) valid++
        }
        
        <!--收缩窗口-->
        // 1、最小覆盖子串:当前种类数等于需要的种类数(结果长度!==需要长度)
        // 2、字符串排列、异位词:当前范围大于等于需要的长度(结果长度===需要长度)
        // 3、无重复子串:窗口属性个数window[r] > 1
        while (valid === Object.keys(need).length) {
        // while (right - left >= t.length)
        // while (window[r] > 1)
            const l = s[left]
            <!--判断当前满足条件则处理结果-->
            // 1、最小覆盖子串:如果当前范围小于上次算的子串长度,则更新最小覆盖子串开始索引和长度
            if (right - left < len) {
                start = left
                len = right - left
            }
            // 2、字符串排列、异位词:如果种类数===需要对象属性长度,则产生结果
            if (valid === Object.keys(need).length) {
                return true
                // 异位词: res.push(left)
            }
            // 3、无重复子串无需处理:再外部循环判断
            <!--左索引移动-->
            left++
            
            <!--更新窗口-->
            // 当前字符串需要,如果窗口数===需要数,种类--,下次则退出循环
            // 窗口数--
            // 1、2、3:最小覆盖子串、字符串排列、异位词
            if (need[l]) {
                if (window[l] === need[l]) valid--
                window[l]--
             }
             // 4:无重复子串只需
             window[l]--
        }
        // 无重复子串在收缩窗口后更新结果
         res = Math.max(res, right - left)
    }
    <!--while循环后返回结果-->
    return len === Infinity ? '' : s.substr(start, len)
    return false
    return res
}

实例

76.最小覆盖子串

const s = 'agblalbu'
const t = 'ab'
console.error(this.minCoverSubstring(s, t))
输出:agb

minCoverSubstring (s, t) {
  // 定义需要字符串个数的对象、当前窗口需要字符串个数的对象
  const need = {}
  const window = {}
  // 定义左右索引、当前种类数
  let left = 0
  let right = 0
  let valid = 0
  // 定义最小字符串的起始索引和长度
  let start = 0
  let len = Infinity
  <!--需要对象赋值-->
  for (const i of t) {
    need[i] = (need[i] || 0) + 1
  }
  
  // 遍历子串
  while (right < s.length) {
    // 右索引右移
    const r = s[right]
    right++
    // 如果当前字符串在需要列表中则当前窗口对应的字符串的个数加1
    if (need[r]) {
      // window[r]++
      window[r] = (window[r] || 0) + 1
      // 如果当前字符串需要的个数已全,则种类数加1
      if (window[r] === need[r]) valid++
    }
    // 判断收缩窗口(当前种类数等于需要的种类数)
    while (valid === Object.keys(need).length) {
      // 更新最小覆盖子串
      if (right - left < len) {
        start = left
        len = right - left
      }
      // 左索引右移
      const l = s[left]
      left++
      // 如果当前字符串在需要的窗口,当前窗口的种类数-1,且如果当前字符的种类数和需要种类数,种类数-1,
      // 一旦减1则不再满足条件,退出循环
      if (need[l]) {
        if (window[l] === need[l]) valid--
        window[l]--
      }
    }
  }
  return len === Infinity ? '' : s.substr(start, len)
}

567.字符串的排列

const s1 = 'agbalapbu'
const t1 = 'ab'
console.error(this.stringRank(s, t))
输出:true

stringRank (s, t) {
  // 定义需要字符串个数的对象、当前窗口需要字符串个数的对象、结果
  const need = {}
  const window = {}
  // 定义左右索引、种类值
  let left = 0
  let right = 0
  let valid = 0
  // 需要对象赋值
  for (const i of t) {
    need[i] = (need[i] || 0) + 1
  }

  // 遍历s字符串
  while (right < s.length) {
    const r = s[right]
    right++
    // 右移右索引,如果当前字符串为需要的则更新当前窗口,更新后如果个数与需要相同,则种类数+1
    if (need[r]) {
      window[r] = (window[r] || 0) + 1
      if (window[r] === need[r]) valid++
    }
    // 当当前范围大于等于t长度,则收缩窗口或判断结果
    while (right - left >= t.length) {
      // 如果种类数等于需要对象长度,则true,
      // 否则如果当窗口数为种类数,则种类数减1、当前窗口数减1,继续缩小窗口
      const l = s[left]
      if (valid === Object.keys(need).length) {
        return true
      }
      left++
      if (need[l]) {
        if (window[l] === need[l]) valid--
        window[l]--
      }
    }
  }
  return false
}

438.找到字符串中所有字母的异位词

const s2 = 'cabhbacj'
const t2 = 'abc'
console.error(this.HeterotopicWords(s2, t2))
输出:[0, 4]

HeterotopicWords (s, t) {
  // 定义需要、当前
  const need = {}
  const window = {}
  const res = []
  // 定义左右索引,种类数
  let left = 0
  let right = 0
  let valid = 0
  // 赋值
  for (const i of t) {
    need[i] = (need[i] || 0) + 1
  }
  // 遍历s
  while (right < s.length) {
    // right++
    const r = s[right]
    right++
    // 如果当前值为需要,窗口window++,如果窗口window和需要need的个数相同,则种类++
    if (need[r]) {
      window[r] = (window[r] || 0) + 1
      if (window[r] === need[r]) valid++
    }
    // 收缩窗口(注意>=)
    while (right - left >= t.length) {
      const l = s[left]
      // 种类数valid为需要数则索引入数组
      if (valid === Object.keys(need).length) {
        res.push(left)
        // res.push(s.substr(left, t.length))
      }
      left++
      // 如果当前为需要,且如果窗口数=需要数,种类数--,窗口数--
      if (need[l]) {
        if (window[l] === need[l]) valid--
        window[l]--
      }
    }
  }
  return res
},

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

const s3 = 'pwwkew'
console.error(this.maxNoRepeatSubString(s3))
输出:3 (wke)

maxNoRepeatSubString(s) {
  // 定义需要、当前窗口、左右索引、结果
  const window = {}
  let left = 0
  let right = 0
  let res = 0
  // 遍历s
  while (right < s.length) {
    const r = s[right]
    right++
    // 窗口更新
    window[r] = (window[r] || 0) + 1

    // 收缩
    while (window[r] > 1) {
      const l = s[left]
      left++
      window[l]--
    }
    res = Math.max(res, right - left)
  }
  return res
}
```/