数组--滑动窗口

滑动窗口大多用于处理连续子数组问题,然后得到相应的长度。
例题:长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

思路:使用滑动窗口i在左边,j在右边,明确意义[i,j]为滑动窗口(闭区间)。如果滑动窗口内的和小于s,则j++来扩大窗口范围。否则说明达到题目要求,记录此时的长度,然后i++,使窗口缩小。重复以上步骤直到i到达数组末尾,注意不要让j越界。
时间复杂度:O(n)

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int tmp = 0;
        for(int n:nums){
            tmp+=n;
        }
        if(tmp<s) return 0; //假如不存在,返回0

        int i = 0;
        int j = 0;
        int sum = nums[0];
        int res = Integer.MAX_VALUE;
        while(i<nums.length){
            if(sum<s){  //和不够大
                if(j<nums.length-1){ //当j未越界,移动j
                    sum+=nums[++j];
                }else{  //如果越界了,移动i
                    sum-=nums[i++];
                }
            }else{
                res = Math.min(res,j-i+1);
                sum-=nums[i++];
            }
        }
        return res;
    }
}
1.无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

还是一样,使用滑动窗口,假如未出现重复元素,则j++扩大窗口,假如出现了则i++缩小窗口。未重复的时候记录当前长度,最后输出最长长度。
注意,没有说过只有小写字母,所以用个set。

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

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:

字母异位词指字母相同,但排列不同的字符串。 不考虑答案输出的顺序。

本来我也很不解,想了半天滑动窗口,有思路但是没有具体措施。然后看了看评论,突然发现和前两个题的不同之处,那就是要找的子串长度固定,这也就是说用的是一个固定大小的滑动窗口,然后每次判断是否为字母异位词就好了。
判断的方式:对目的字符串用一个int数组统计每个字母出现次数,然后对于滑动窗口内的字符串也进行统计,最后比较两个数组即可。

lass Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list = new LinkedList<Integer>();
        if(s.length()<p.length()) return list;

        int i = 0;
        int j = p.length()-1;
        int[] nums = new int[26];
        for(int k=0;k<p.length();k++){
            nums[p.charAt(k)-'a']++;
        }
        while(j<s.length()){
            int[] tmp = new int[26];
            for(int k=i;k<=j;k++){
                tmp[s.charAt(k)-'a']++;
            }
            if(Arrays.equals(nums,tmp)) list.add(i);
            i++;
            j++;
        }
        return list;
    }
}

代码可行,不过涉及数组的比较,于是运行十分缓慢,想到的第一个优化点是,如果遍历到了根本不属于目标字符串的字母,那么直接滑动窗口跳到它的后面。这样一来,时间消耗减少了一半。

3.最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:

输入: S = “ADOBECODEBANC”, T = “ABC” 输出: “BANC”

说明: 如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

首先,用我薄弱的大脑想一想,可以设置一个哈希表,然后存储T中所有字符的对应出现次数。然后依然是不固定大小的滑动窗口遍历,如果未满足要求,就j++,满足要求就i++,每次满足要求时存储一下ij的值,根据j-i的大小取舍。
那么怎么判断是否满足条件呢?规定每次添加一个元素x,如果它在map中,那么值-1;每次删除一个元素x,如果它在map中,那么值+1。如果此时map中的值全为0,说明以及符合条件。

class Solution {
    public String minWindow(String s, String t) {
        Map<Character,Integer> map = new HashMap<Character, Integer>();
        List<String> list = new ArrayList<>();
        int count = 0;  //count用来大致判断hashmap是否全为0,当然会有偏差,不过可以适当减少遍历map的次数
        for(int i=0;i<t.length();i++){
            map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0)+1);
            count++;
        }
        int i = 0;
        int j = -1;
        int resj = s.length()-1;
        int resi = 0;
        boolean flag = true;   //判断是否不存在这样的子串
        while(j<s.length()){
            if(count<=0 && isEmpty(map)){   //此时满足要求
                if(j-i<resj-resi){  //如果新的子串比旧的短,就存储
                    resi = i;
                    resj = j;
                }
                if(map.containsKey(s.charAt(i))){   //如果移除掉的是指定元素,则向map中添加
                    flag = false;
                    map.replace(s.charAt(i),map.get(s.charAt(i))+1);
                    count++;
                }
                i++;    //滑动窗口缩小
            }else{  //不符合的情况下
                j++;
                if(j<s.length()){
                    if(map.containsKey(s.charAt(j))){
                        map.replace(s.charAt(j),map.get(s.charAt(j))-1);
                        count--;
                    }
                }
            }
        }
        if(flag) return "";
        return s.substring(resi,resj+1);
    }
    public boolean isEmpty(Map<Character,Integer> map){
        for(Integer i:map.values()){
            if(i>0) return false;
        }
        return true;
    }
}

你可能感兴趣的:(leetcode,数组,滑动窗口)