滑动窗口解决子串问题(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)
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值