滑动窗口解决子串问题(js)

共同点
  1. 记录窗口中出现的字符及次数 casement
  2. 记录题目要求的字符及出现次数 need
  3. 判断窗口中符合要求的字符出现总数 valid
  4. 对新增和移出字符在窗口中出现次数的处理,对符合要求字符总数的更改
题目差异
  1. 收缩窗口的条件
  2. 记录答案的条件
五道题目

76.最小重复子串

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
    //casement记录窗口中的字符以及出现的次数
    //need统计子串包括的字符以及字数
    let need = new Map()
    let casement = new Map()
    //valid变量处理casement中出现的符合要求need的字符的个数
    let valid = 0
    //left right指针代表窗口[left,right) 在初始情况下窗口中没有值
    let [left,right] = [0,0]
    //储存子串起点以及长度 将length赋值为最大整型值 为了方便第一次记录length长度
    let [start,length] = [0,Number.MAX_SAFE_INTEGER]
    for(let str of t){
        need.set(str,need.get(str) ? need.get(str) + 1 : 1)
    }
    while(right < s.length){
        //新增加到窗口中的字符是 s[right]
        let newChar = s[right]
        //把新增的字符加到casement里面
        casement.set(newChar,casement.get(newChar) ? casement.get(newChar)+1 : 1)
        //判断这个字符在不在need里面 以及新加上的这个字符在窗口里出现的总次数仍然小于need要求该字符出现的次数 说明满足条件的字符多了一个
        if(casement.get(newChar) <= need.get(newChar)){
            valid++
        }
        right++
        //当首次窗口中出现的字符等于need要求的字符个数 窗口左侧向右移
        while(valid == t.length){
            //在窗口即将缩小的时候 记录当前子串 
            //这里的if判断相当于把这次的子串和上次的子串长度作对比取最小
            if(right - left < length){
                start = left
                length = right - left
            }
            //移出窗口的字符 s[left]
            let moveChar = s[left]
            //根据need判断是否需要这个字符
            if(need.get(moveChar)){
                if(casement.get(moveChar) == need.get(moveChar)){
                    valid--
                }
                casement.set(moveChar,casement.get(moveChar)-1)
            }
            left++
        }
    }
    //如果length仍是初始化值 说明从来没有满足条件的子串出现
    return length == Number.MAX_SAFE_INTEGER ? "" : s.substr(start,length)
};

567.字符串的排列

/**
 * @param {string} s1
 * @param {string} s2
 * @return {boolean}
 */
var checkInclusion = function(s1, s2) {
    //casement记录窗口中字符的出现次数 need记录s1要求的字符出现次数
    let casement = new Map()
    let need = new Map()
    for(let char of s1){
        need.set(char,need.get(char) ? need.get(char)+1 : 1)
    }
    //left right表示窗口左右侧下标 窗口中包括的字符为[left,right)
    let [left,right] = [0,0]
    let valid = 0  //储存窗口中满足条件的字符个数
    while(right < s2.length){
        // 新增的字符为s2[right]
        let newChar = s2[right]
        right++
        //更新casement中该字符的次数
        casement.set(newChar,casement.get(newChar) ? casement.get(newChar)+1 : 1)
        //根据need判断 窗口中满足条件的字符个数是否增加
        if(casement.get(newChar) <= need.get(newChar)){
            valid++
        }

        //当窗口长度大于s1要求的长度时  开始缩小窗口(和)
        while(right - left >= s1.length){
            if(valid == s1.length) return true
            //从窗口移出去的字符为 s2[left]
            let moveChar = s2[left]
            left++
            //更新casement need对应字符
            casement.set(moveChar,casement.get(moveChar)-1)
            //这里判断不取等 是因为如果删掉那个字符 窗口中这个字符出现的次数仍然等于need中要求的出现次数 说明删掉的字符不影响valid valid就不用减一
            if(casement.get(moveChar) < need.get(moveChar)){
                valid--
            }
            console.log(moveChar,)
        }
    }
    return false
};

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

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    let casement = new Map()  //记录窗口中的字符以及出现次数
    let need = new Map()      //记录题目中要求的字符以及出现次数
    let ans = []              //要返回的答案数组
    let [left,right] = [0,0]  //窗口的左右指针 casement记录的字符为[left,right)
    let valid = 0             //记录窗口中符合要求的字符总次数
    for(let char of p){
        need.set(char,need.get(char) ? need.get(char) +1 : 1)
    }
    while(right < s.length){
        //新增的字符为s[right]
        let newChar = s[right]
        right++
        //新字符加入窗口
        casement.set(newChar,casement.get(newChar) ? casement.get(newChar)+1 : 1)
        // 根据need中是否要求该字符 修改valid
        if(need.get(newChar)){
            if(casement.get(newChar) <= need.get(newChar)){
                valid++
            }
        }
        //当收集满要求的总次数 要开始收缩窗口 当窗口长度等于要求的长度时 把答案添加到ans
        while(valid == p.length){
            if(right - left == p.length) ans.push(left)
            let moveChar = s[left]   //移出窗口的元素为s[left]
            left++
            casement.set(moveChar,casement.get(moveChar) ? casement.get(moveChar)-1 : 1)
            if(casement.get(moveChar) < need.get(moveChar)){
                valid--
            }
        }
    }
    return ans
};

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

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let casement = new Map()   //记录窗口中的字符以及是否该字符出现次数
    let [left,right] = [0,0]
    let ans = 0
    while(right < s.length){
        let newChar = s[right]
        casement.set(newChar,casement.get(newChar) ? casement.get(newChar)+1 : 1)
        right++
        while(casement.get(newChar) > 1){
            // 当窗口中始终包括这个新字符 就要一直收缩窗口
            let moveChar = s[left]
            casement.set(moveChar,casement.get(moveChar)-1)//去掉移出字符
            left++
        }
        ans = right-left > ans ? right-left : ans
    }
    return ans
};


//方法二
//这个题用js原生的字符串方法我觉得更好理解一点 但中心思想也是维护一个没有重复字符的窗口
var lengthOfLongestSubstring = function(s) {
    let str = '',
        ans = 0
    for(let i = 0;i < s.length;i++){
        //判断当前最长不重复子串里面有没有这个字符
        if(str.indexOf(s[i]) != -1){
            //如果最长字串里面有这个
            str = str.slice(str.indexOf(s[i])+1) + s[i]
        }else{
            str += s[i]
        }
        ans = Math.max(ans,str.length)
    }
    return ans
};

187. 重复的DNA序列

/**
 * @param {string} s
 * @return {string[]}
 */
var findRepeatedDnaSequences = function(s) {
    let casement = new Set()
    let ans = []
    let str = ''
    let [left,right] = [0,0]
    while(right < s.length){
        //新增字符
        str += s[right]
        right++
        if(str.length == 10){
            //如果子串长度等于10  就要判断casement里面有没有这个子串了
            if(casement.has(str)){
                ans.push(str)
            }else{
                casement.add(str)
            }
            //填进去之后要收缩窗口了
            str = str.replace(s[left],'')
            left++
        }
        
    }
    //答案去重
    let newSet = new Set()
    ans.map(item=>{
        newSet.add(item)
    })
    //类数组转数组 返回答案
    return Array.from(newSet)
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
滑动窗口算法是一种用于解决一些查找满足一定条件的连续区间的性质的问题的思想或技巧。它可以将双层嵌套的循环问题转换为单层遍历的循环问题,从而降低时间复杂度。滑动窗口算法的步骤通常包括维护两个指针left和right,表示当前窗口的左右边界。通过移动右指针扩大窗口,直到窗口内的元素之和满足某个条件。然后,移动左指针缩小窗口,直到不能再缩小为止。在这个过程中,记录窗口的最小长度,并更新最小长度的值。最后返回最小长度。滑动窗口算法的优点包括时间复杂度较低、空间复杂度较低、简单易懂。然而,滑动窗口算法也有一些缺点,包括无法解决所有问题、可能存在重复计算和可能存在局限性。总的来说,滑动窗口算法是一种有效的解决特定类型问题的方法。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [滑动窗口算法精讲(Sliding Window Algorithm)](https://blog.csdn.net/qq_39559641/article/details/122793321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [滑动窗口算法](https://blog.csdn.net/m0_63951142/article/details/130671127)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值