滑动窗口

记录一下自己刷题时遇到的题目,这里的解并不一定是最优的,只是贴出来一种解答方法。

滑动窗口常用来扫描找到满足特定条件的解, 通过(left, right两个指针)来维护一个长度的窗口,通过right指针来扫描扩充窗口,当窗口内的元素不满足条件时,通过left指针来调整窗口。条件值和当前值可以用HashMap来记录、维护,由于题目往往只涉及到字母、数字,所以可以用数组来代替哈希表,以空间换时间。

  1. 424,Longest Repeating Character Replacement

    count数组记录当前窗口内各字符出现的次数。max+k即为当前窗口大小,只有当前窗口内的max增加时才会变大,否则维持当前大小。因为窗口大小不会缩小,所以当遍历结束后,当前窗口即为最大的窗口。

    public int characterReplacement(String s, int k) {
        int[] count = new int[26];
        int max = 0, left = 0;
        for (int i = 0; i < s.length() ; i++) {
            int index = s.charAt(i) - 'A';
            count[index]++;
            max = Math.max(max, count[index]);
            if (i - left + 1> max + k){
                count[s.charAt(left) - 'A']--;
                left++;
            }
        }
        //because we never shrink the window, so the window size is biggest at the end.
        return s.length() - left;
    }
    
  2. 76.Minimum Window Substring

    except记录期望出现的字符,seen记录当前窗口内出现的字符(其实可以只用一个数组),count表示当前窗口内满足的字符个数。当窗口内存在满足条件的字符串,则收缩窗口(调整left指针),并记录最小字符串的位置。

    public static String minWindow(String s, String t) {
        int[] except = new int[128];
        int[] seen = new int[128];
        for (char c: t.toCharArray()) {
            except[c]++;
        }
        int left = 0, count = 0, minLeft = 0, minLen = Integer.MAX_VALUE;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            seen[c]++;
            if (seen[c] <= except[c]){
                count++;
            }
            while (count >= t.length()){
                int len = i - left + 1;
                if (len < minLen){
                    minLeft = left;
                    minLen = len;
                }
                char startChar = s.charAt(left);
                seen[startChar]--;
                if (seen[startChar] < except[startChar]){
                    count--;
                }
                left++;
            }
        }
        return minLen == Integer.MAX_VALUE? "" : s.substring(minLeft,minLeft + minLen);
    }
    
  3. 3.Longest Substring Without Repeating Characters

    因为只期望每个字符出现一次,所以重复字符出现时,没必要用while循环去确定left位置,可以用count或map记录字符出现的位置,重复字符出现时,直接将left置为map[c]+1。

    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.equals("")){
            return 0;
        }
        int[] count = new int[128];
        int left = 0, maxLen = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            count[c]++;
            if (count[c] == 1){
                maxLen = Math.max(maxLen, i - left + 1);
            }
            while (count[c] > 1){
                char startChar = s.charAt(left);
                count[startChar]--;
                left++;
            }
        }
        return maxLen;
    }
    
  4. 1004.Max Consecutive Ones III

    424: Longest Repeating Character Replacement

    public int longestOnes(int[] A, int K) {
        int oneCount = 0, left = 0, max = 0;
        for (int i = 0; i < A.length; i++) {
            if (A[i] == 1){
                oneCount++;
            }
            max = Math.max(max, oneCount);
            if (i - left + 1 > max + K){
                if (A[left] == 1){
                    oneCount--;
                }
                left++;
            }
        }
        return  A.length - left;
    }
    
  5. 567. Permutation in String

    固定窗口大小为目标字符数,本次用except代替except和seen两个数组,用count记录出现的目标字符数。思路同76: Minimum Window Substring,只不过将窗口由可变长度变为固定长度。

    public boolean checkInclusion3(String s1, String s2) {
        int[] except = new int[26];
        for(char c:s1.toCharArray()){
            except[c -'a']++;
        }
        int left = 0, count = 0;
        for (int i = 0; i < s2.length(); i++) {
            int index = s2.charAt(i) - 'a';
            if (except[index] > 0){
                count++;
            }
            except[index]--;
            if (count == s1.length()){
                return true;
            }
            if (i - left + 1 >= s1.length()){
                int beginIndex = s2.charAt(left) - 'a';
                except[beginIndex]++;
                if (except[beginIndex] > 0){
                    count--;
                }
                left++;
            }
        }
        return false;
    }
    
  6. 438. Find All Anagrams in a String

    固定长度的滑动窗口,思路同567. Permutation in String

    public static List<Integer> findAnagrams(String s, String p) {
        List<Integer> rs = new ArrayList<>();
        int exceptLen = p.length();
        int[] except = new int[26];
        for(char c: p.toCharArray()){
            except[c-'a']++;
        }
        int left = 0, count = 0;
        for (int i = 0; i < s.length(); i++) {
            int index = s.charAt(i) - 'a';
            if (except[index] > 0){
                count++;
            }
            except[index]--;
            if (i - left + 1 >= p.length()){
                if (count == exceptLen){
                    rs.add(left);
                }
                int leftIndex = s.charAt(left) - 'a';
                except[leftIndex]++;
                if (except[leftIndex] > 0){
                    count--;
                }
                left++;
            }
        }
        return rs;
    }
    
  7. 239. Sliding Window Maximum

    用最大堆时间效率稍高,因为总是维护k个元素的堆。可以用双端队列dequeue存储下标,并及时维护(删除窗口外下标、删除值小于将要进窗口的元素的下标)来减少所用时间。

    public static int[] maxSlidingWindow(int[] nums, int k) {
        int[] rs = new int[nums.length - k + 1];
        Queue<Integer> priorityQueue = new PriorityQueue<Integer>(k,((o1, o2) -> o2 - o1));
        int left = 0;
        for (int i = 0; i < nums.length; i++) {
            priorityQueue.add(nums[i]);
            if (i - left + 1 == k){
                rs[i- k +1] = priorityQueue.peek();
                priorityQueue.remove(nums[left]);
                left++;
            }
        }
        return rs;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值