leetcode——滑动窗口

183 阅读4分钟

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

给定一个字符串 `s` ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

image.png 做这道题出现了两个错误
1.set的大小是size不是length。
2.max = Math.max(set.size,max)这个式子是放在求无重复的set那

var lengthOfLongestSubstring = function(s) {
    let i = 0;
    let j = 0;
    let set = new Set();
    let max = 0;
    if(s.length === 0) return 0;
    for(i; i < s.length; i++){
        // 不断遍历s,如果滑动窗口没有这个元素就加入set
        if(!set.has(s[i])){
            set.add(s[i]);
            max = Math.max(set.size,max)
        }else{
            while(set.has(s[i])){
                set.delete(s[j]);
                j++;
            }
            set.add(s[i]);
        } 
    }
    return max
};

219. 存在重复元素 II

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。
输入: nums = [1,2,3,1], k = 3
输出: true

WechatIMG203.png

题解:不断将元素加入滑动窗口中,也就是加入set,如果set中存在重复元素并且窗口大小小于指定大小就返回,否则加入set中,当滑动窗口超过了指定大小,缩小窗口

var containsNearbyDuplicate = function(nums, k) {
    let set = new Set();
    for(let i = 0; i < nums.length; i++){
        if(set.has(nums[i])){
            return true;
        }
        set.add(nums[i]);
        if(set.size > k){
            set.delete(nums[i-k])
        }
    }
    return false
};

76. 最小覆盖子串

给你一个字符串 `s` 、一个字符串 `t` 。返回 `s` 中涵盖 `t` 所有字符的最小子串。如果 `s` 中不存在涵盖 `t` 所有字符的子串,则返回空字符串 `""` 。
输入: s = "ADOBECODEBANC", t = "ABC"
输出: "BANC"

WechatIMG203.png 思路:这道题目有目标框,我们先设置need框和window框。用左右指针,right指针这个字符x,need有,就加入window里面,当window[x] == need[x],valid+1。当valid==need的长度说明s已经包含了t字符串,这时候我们需要移动left,如果这个left指向y,y是need里面需要的,我们移除y的时候need[y]-1,window[y]-1,valid-1。这个时候就是valid==need的长度不成立了,我们就要跳出循环。值得注意的是我们要求最小子串,所以我们需要start和截取长度,所以我们在满足valid==need的长度时先获取这两个值。

var minWindow = function(s, t) {
    let need = {};
    let window = {};
    for(let x of t){
        need[x] = (need[x] || 0) + 1;
    }
    let right=0,left = 0, start = 0, len = Number.MAX_VALUE, valid = 0;
    while(right < s.length){
        // 先操作右指针
        let x = s[right];
        right++;
        if(need[x]){
            window[x] = (window[x] || 0 ) + 1;
            if(window[x] === need[x]){
                valid++
            }
        }
        // 满足t字符串了
        while(valid === Object.keys(need).length){
            // 先更新最新的
            if(right - left < len){
                start = left;
                len = right - left
            }
            // 操作左指针,来跳出循环从而继续移动右指针
            let y = s[left];
            left++;
            if(need[y]){
                if(need[y] === window[y]){
                    valid--;
                }
                window[y]--;
            }
        }
    }
    return len == Number.MAX_VALUE ? "" : s.substr(start, len);
};

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

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

WechatIMG203.png

var findAnagrams = function(s, p) {
    let need = {};
    let window = {};
    for(let x of p){
        need[x] = (need[x] || 0) + 1;
    }
    let right = 0, left = 0, valid = 0;
    let res = [];
    while(right < s.length){
        let x = s[right];
        right++;
        if(need[x]){
            window[x] = (window[x] || 0) + 1;
            if(need[x] === window[x]){
                valid++;
            }
        }
        // 这里移动左指针的条件是right-left>=p.length
        while(right - left >= p.length){
            if(valid === Object.keys(need).length){
                res.push(left)
            }
            let y = s[left];
            left++;
            if(need[y]){
                if(window[y] === need[y]){
                    valid--;
                }
                window[y]--;
            }
        } 
    }
    return res
};

1456. 定长子串中元音的最大数目

给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
输入: s = "abciiidef", k = 3
输出: 3
解释: 子字符串 "iii" 包含 3 个元音字母。

image.png

var maxVowels = function (s, k) {
    let set = new Set(['a', 'e', 'i', 'o', 'u'])
    let count = 0, l = 0, r = 0;
    while(r < k){
        set.has(s[r]) && count++;
        r++;
    }//这个时候r=3
    let max = count;
    while(r < s.length){
        // 因为现在r是从3开始
        set.has(s[r]) && count++;
        set.has(s[l]) && count--;
        l++;
        r++;
        max = Math.max(max, count)
    }
    return max
};

904. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
输入: fruits = [0,1,2,2]
输出: 3
解释: 可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

WechatIMG203.png 思路:用滑动窗口遍历fruits,当有新种类的水果进入窗口时
1.如果窗口中只有一种水果,将这种水果加入arr数组
2.如果有两种水果,更新窗口的左边界,更新arr中水果的种类
3.如果进来了一种新的类型的水果 更新前一种水果的位置
4.更新滑动窗口的最大值

var totalFruit = function(fruits) {
    let left = 0, right = 0, n = 0;//表示前一个水果刚开始的位置,主要是用来更新left
    let arr = [fruits[0]];
    let max = 0;
    for(right; right < fruits.length; right++){
        // 先加水果再更新left
        if(!arr.includes(fruits[right])){//如果窗口中不包含 进窗口的水果
            if(arr.length <=1 ){//窗口只有一个水果
                arr[1] = fruits[right]
            }else{// 窗口有两个水果了,要加新水果
                //因为只能有两种水果,所所以我们要不断跟新第一个水果的开始位置
                left = n;
                // arr[0] = arr[1]; 不能这样写,比如101414,arr[1]=0,但是我们要的是1
                arr[0] = fruits[right-1]
                arr[1] = fruits[right] 
            }
        }
        // 开始更新 新的位置
        if(fruits[right] !== fruits[n]){
            n = right
        }
        max = Math.max(max,right-left+1)
    }
    return max
};