滑动窗口

167 阅读1分钟
  1. 求出字符串的「字谜」

字谜:两个字符串每个字符出现次数相同只是顺序不同,则互为字谜

tip: 滑动窗口,校验次数

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
  let map = {}, count = 0, ans = [];

  var resetMap = () => {
      for (let key in map) {
          if(map.hasOwnProperty(key)) {
              map[key].count = map[key].originCount;
          }
      }
      count = 0;
  }

  for (let i = 0; i < p.length; i++) {
      if (map[p[i]]) {
          map[p[i]].count++;
          map[p[i]].originCount++;
      } else {
          map[p[i]] = {
              count: 1,
              originCount: 1
          };
      }
  }
  
  let i = 0, j = 0;
  while (j < s.length) {
      if (map[s[j]] && map[s[j]].count > 0) {
          //字符数-1, 匹配数+1
          map[s[j]].count--;
          count++;
          //完成了匹配
          if (count == p.length) {
            ans.push(i);
            j++;
            while(s[i] == s[j]) {
                i++;
                ans.push(i);
                j++;
            }
            map[s[i]].count++;
            count--;
            i++;
          } else {
            j++;
          }
          
      } else {
          if (!map[s[j]]) {
              //不可能从j开始,窗口滑动,从头开始。
              j++;
              i = j;
              resetMap();
          } else {
              while(s[i] != s[j]) {
                  map[s[i]].count++;
                  count--;
                  i++;
              }
              i++;
              j++;
          }
      }
  }  

  return ans;
};
  1. 至多包含k个不同字符的最长子串

解法:维持大小为k的滑动窗口。 遇到元素出现过,无脑移动 遇到元素未出现过,对窗口左侧进行收敛 利用map记录元素出现的次数

/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var lengthOfLongestSubstringKDistinct = function(s, k) {
    if (k == 0) return 0;

    let map = {}, max = 0, count = 0;

    let i = 0, j = 0;
    //滑动窗口。保持窗口内只有K个不同字符
    while (j < s.length) {
        //s[j]没有出现过
        if (!map[s[j]]) {
            //字符计数+1
            count++;
            map[s[j]] = 1;
            //如果窗口超过k,那么左边需要收敛,直到窗口大小重新变成K
            while (count > k) {
                map[s[i]]--;
                if (map[s[i]] == 0) {
                    count--;
                }
                i++;
            }
        } else {
            //出现过,则无脑向前,计数+1
            map[s[j]]++;
        }
        max = Math.max(max, j - i + 1);
        j++;  
    }

    return max;
};
  1. 长度最短的和为k的连续子序列

序列连续则考虑滑动窗口

left, right初始为0,right依次向右求和 和小于k, right向右 和大于k,left向右 等于k时,不断比较长度

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    var min = nums.length, left = 0, right = -1, sum = 0, flag = false;
    nums.forEach((num, index) => {
        sum += num;
        right++;
        while(sum >= target) {
          flag = true;
          if (right - left + 1 < min) {
             min = right - left + 1;
          }
          sum -= nums[left];
          left++;
        }
    });
    
    return flag ? min : 0;
};