JS算法练习3.11

本文介绍了滑动窗口的解题思路,并通过LeetCode的四个问题(209.长度最小的子数组,3.无重复字符的最长子串,438.找到字符串中所有字母异位词,76.最小覆盖子串)来具体阐述如何使用滑动窗口解决字符串处理问题。在每个例子中,都展示了如何通过调整左右指针来找到符合条件的子串,并优化解题过程。
摘要由CSDN通过智能技术生成

滑动窗口的解题思路

  • 我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。
  • 我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 t 中的所有字符)
  • 我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果
  • 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头

解题框架:

 // 右移窗口
    right++;
    if (need[c]) {
      // 当前字符在需要的字符中,则更新当前窗口统计
      window[c] = (window[c] || 0) + 1;
      if (window[c] == need[c]) {
        // 当前窗口和需要的字符匹配时,验证数量增加1
        valid++;
      }
    }
    // 当验证数量与需要的字符个数一致时,就应该收缩窗口了
    while (valid == Object.keys(need).length) {
      // 更新最小覆盖子串
      if (right - left < len) {
        start = left;
        len = right - left;
      }
      //即将移出窗口的字符
      let d = s[left];
      // 左移窗口
      left++;
      if (need[d]) {
        if (window[d] == need[d]) {
          valid--;
        }
        window[d]--;
      }
    }
  }
  return len == Number.MAX_VALUE ? "" : s.substr(start, len);
};

Leetcode 209 长度最小的子数组

/*
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let len=nums.length;
    let left=0,right=-1;//初始希望滑动窗里没有任何元素
    let res=len+1;
    let sum=0;

    while(left<len){
        if(right+1<len&&sum<target) sum+=nums[++right];
        else sum-=nums[left++];
       
        if(sum>=target) res=Math.min(res,right-left+1);
    }

    if(res===len+1) return 0;
    return res;
};

Leetcode 3 无重复字符的最长字串

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    const occ = new Set();
    const n = s.length;
    let rk = -1, ans = 0;

    for (let i = 0; i < n; ++i) {
        if (i != 0) occ.delete(s[i-1]);
    
        while (rk + 1 < n && !occ.has(s[rk + 1])) {
            occ.add(s[rk + 1]);
            ++rk;
        }

        ans = Math.max(ans, rk - i + 1);
    }
    return ans;
};

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

根据题目要求,我们需要在字符串 ss 寻找字符串 pp 的异位词。因为字符串 pp 的异位词的长度一定与字符串 pp 的长度相同,所以我们可以在字符串 ss 中构造一个长度为与字符串 pp 的长度相同的滑动窗口,并在滑动中维护窗口中每种字母的数量;当窗口中每种字母的数量与字符串 pp 中每种字母的数量相同时,则说明当前窗口为字符串 pp 的异位词。

var findAnagrams = function(s, p) {
    const sLen = s.length, pLen = p.length;

    if (sLen < pLen) {
        return [];
    }

    const ans = [];
    const sCount = new Array(26).fill(0);
    const pCount = new Array(26).fill(0);
    for (let i = 0; i < pLen; ++i) {
        ++sCount[s[i].charCodeAt() - 'a'.charCodeAt()];
        ++pCount[p[i].charCodeAt() - 'a'.charCodeAt()];
    }

    if (sCount.toString() === pCount.toString()) {
        ans.push(0);
    }

    for (let i = 0; i < sLen - pLen; ++i) {
        --sCount[s[i].charCodeAt() - 'a'.charCodeAt()];
        ++sCount[s[i + pLen].charCodeAt() - 'a'.charCodeAt()];

        if (sCount.toString() === pCount.toString()) {
            ans.push(i + 1);
        }
    }

    return ans;
};

Leetcode 76 最小覆盖子串

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
const minWindow = (s, t) => {
  let minLen = s.length + 1;
  let start = s.length;     // 结果子串的起始位置
  let map = {};             // 存储目标字符和对应的缺失个数
  let missingType = 0;      // 当前缺失的字符种类数

  for (const c of t) {      // t为baac的话,map为{a:2,b:1,c:1}
    if (!map[c]) {
      missingType++;        // 需要找齐的种类数 +1
      map[c] = 1;
    } else {
      map[c]++;
    }
  }
  
  let l = 0, r = 0;                // 左右指针
  for (; r < s.length; r++) {      // 主旋律扩张窗口,超出s串就结束
    let rightChar = s[r];          // 获取right指向的新字符
    if (map[rightChar] !== undefined) map[rightChar]--; // 是目标字符,它的缺失个数-1
    if (map[rightChar] == 0) missingType--;   // 它的缺失个数新变为0,缺失的种类数就-1
    while (missingType == 0) {                // 当前窗口包含所有字符的前提下,尽量收缩窗口
      if (r - l + 1 < minLen) {    // 窗口宽度如果比minLen小,就更新minLen
        minLen = r - l + 1;
        start = l;                 // 更新最小窗口的起点
      }
      let leftChar = s[l];          // 左指针要右移,左指针指向的字符要被丢弃
      if (map[leftChar] !== undefined) map[leftChar]++; // 被舍弃的是目标字符,缺失个数+1
      if (map[leftChar] > 0) missingType++;      // 如果缺失个数新变为>0,缺失的种类+1
      l++;                          // 左指针要右移 收缩窗口
    }
  }
  if (start == s.length) return "";
  return s.substring(start, start + minLen); // 根据起点和minLen截取子串
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值