015、滑动窗口(labuladong)

滑动窗口算法

记录labuladong的算法小抄,网站地址

1、概述

滑动窗口的核心是利用两个指针,维护一个窗口,不断滑动,时间复杂度o(N);
重点:

  • 向窗口中添加新的元素
  • 缩小窗口
  • 窗口滑动的哪个阶段更新结果
  • Integer和String不能直接用==进行相等判断,需要用equals方法

框架:

    // 滑动窗口算法框架
    void slidingWindow(String s) {
        HashMap<Character, Integer> window;

        int left = 0, right = 0;
        while (right < s.length()) {
            // c 是将移入窗口的字符
            char c = s.charAt(right);
            // 增大窗口
            right++;
            // 进行窗口内数据的一系列更新
            ...

            // 判断左侧窗口是否要收缩
            while (window needs shrink){
                // d 是将移出窗口的字符
                char d = s[left];
                // 缩小窗口
                left++;

                // 进行窗口内数据的一系列更新
                ...
            }
        }
    }

2、最小覆盖字串

力扣第76题,最小覆盖字串,hard级别
滑动窗口思路如下:

  • 采用左闭右开区间[left,right),方便使用==substring()==方法
    • [0,0):没有元素
    • [0,1):含有元素0
  • 先不断增加right指针,直到窗口[left,right)中包含目标字符串(找到一个可行解)
  • 此时,停止增加right,不断增加left,缩小窗口[left,right),直到窗口的字符串不再符合要求时更新新的一轮结果(优化可行解,找到最优解)
  • 重复步骤2-3,直至right到达尽头

本题关键点:

  • ASCII表总共有128个元素,初始两个数组分别作为字符串s和t的记录
  • 右滑后再去进行滑动完的操作
  • 先判断left,操作完毕后,进行左滑

代码:

[76]最小覆盖子串

class Solution {
    public String minWindow(String s, String t) {
        // 先行条件判断
        if (s.length() < t.length()) {
            return "";
        }

        int[] window = new int[128];// 记录滑动窗口中字符出现的次数
        int[] need = new int[128];// 记录t中所有字符的出现的次数

        for (char c : t.toCharArray()) {
            need[c]++;
        }

        String result = "";// 返回的结果
        int left = 0;// 左指针
        int right = 0;// 右指针
        int count = 0;// 滑动窗口中包含字符串t字符的个数
        int minLength = s.length() + 1;// 返回结果的字符串的长度

        // 左闭右开区间[left,right)
        while (right < s.length()) {
            char cur = s.charAt(right);
            right++;
            // 窗口右滑扩大做的操作
            window[cur]++;
            if (need[cur] > 0 && need[cur] >= window[cur]) {
                count++;
            }
            // 窗口缩减的情况
            while (count == t.length()) {
                cur = s.charAt(left);
                // 先判断当前left位置处的元素
                // 判断count是否需要变小
                if (need[cur] > 0 && need[cur] >= window[cur]) {
                    count--;
                    window[cur]--;
                } else {
                    window[cur]--;
                }
                // 更新结果
                if (right - left < minLength) {
                    result = s.substring(left, right);
                    minLength = right - left;
                }
                // 判断完毕后进行左滑
                left++;
            }
        }
        return result;
    }
}

3、字符串排列

力扣第567题,字符串的排列

[567]字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int[] window = new int[128];
        int[] need = new int[128];
        // 将s1的字符串遍历放入need中
        for (char c : s1.toCharArray()) {
            need[c]++;
        }
        int count = 0;
        // 定义left和right指针,组成滑动窗口
        int left = 0, right = 0;
        while (right < s2.length()) {
            char cur = s2.charAt(right);
            right++;
            // 右滑后的操作
            window[cur]++;
            if (need[cur] > 0 && need[cur] >= window[cur]) {
                count++;
            }
            // 收缩窗口的时机
            while (count == s1.length()) {
                // 判断,即此时[left,right)为s1
                if (right - left == count) {
                    return true;
                }
                cur = s2.charAt(left);
                // 判断是否需要缩小count
                if (need[cur] > 0 && need[cur] >= window[cur]) {
                    count--;
                    window[cur]--;
                } else {
                    window[cur]--;
                }
                left++;
            }
        }
        // 结果false
        return false;
    }
}

4、找所有字母异位词

力扣438题,找到字符串中所有字母异位词

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

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 结果
        List<Integer> result = new ArrayList<>();
        // 两个数组
        int[] window = new int[26];
        int[] need = new int[26];
        for (char c : p.toCharArray()) {
            need[c - 'a']++;
        }
        // 指针和计数器
        int left = 0, right = 0, count = 0;
        // 开始窗口滑动
        while (right < s.length()) {
            char cur = s.charAt(right);
            right++;
            // 右滑后的操作
            window[cur - 'a']++;
            if (need[cur - 'a'] > 0 && need[cur - 'a'] >= window[cur - 'a']) {
                count++;// 判断计数器是否需要更新
            }
            // 满足条件时
            while (count == p.length()) {
                // 处理结果
                if (right - left == count) {
                    result.add(left);
                }
                // 窗口左滑
                cur = s.charAt(left);
                if (need[cur - 'a'] > 0 && need[cur - 'a'] >= window[cur - 'a']) {
                    count--;
                    window[cur - 'a']--;
                } else {
                    window[cur - 'a']--;
                }
                left++;
            }
        }
        // 返回结果集
        return result;
    }
}

5、最长无重复子串

力扣第3题,无重复字符的最长子串

[3]无重复字符的最长子串

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int result = 0;// 结果
        int[] window = new int[128];// 滑动的窗口
        int left = 0, right = 0;

        while (right < s.length()) {
            char rightChar = s.charAt(right);
            right++;
            // 窗口右滑后的操作
            window[rightChar]++;
            if (window[rightChar] <= 1) {
                result = right - left > result ? right - left : result;// 更新结果
            }
            // 满足条件时
            while (window[rightChar] > 1) {
                char leftChar = s.charAt(left);
                window[leftChar]--;
                left++;
            }
        }

        return result;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值