滑动窗口入门讲解

滑动窗口

思想:

滑动窗口算法的思想是通过维护一个窗口,在一个序列上滑动该窗口以寻找特定条件的子序列。

也有用虫取法更生动形象:滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:后脚不动,把前脚向前移动;前脚不动,把后脚移动过来。

滑动窗口算法的基本思路如下:

  1. 定义两个指针left和right,分别指向窗口的左右边界。
  2. 移动右指针,扩大窗口,直到满足特定条件为止。
  3. 移动左指针,缩小窗口,直到不满足特定条件为止。
  4. 重复步骤2和步骤3,直到右指针到达序列的末尾。

滑动窗口算法的优势在于可以避免对每个子序列进行暴力枚举,从而提高时间效率。它的应用范围非常广泛,如字符串匹配、最小覆盖子串、最长无重复子串等问题都可以使用滑动窗口算法进行解决。

引入简单示例:

Leetcode 209. 长度最小的子数组

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

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

完全符合上边的思路,按照思路写:java

思路:

public class 长度最小的子数组 {
    public int minSubArrayLen(int target, int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }//nums为空或长度为0直接返回不存在
        int left = 0, right = 0;//定义初始值从0开始
        int sum = 0, minLen = 100001;//最小长度定义为大数
        while (right < nums.length) {
        //右边框从左往右增大
            sum += nums[right];
            right++;
            while (sum >= target) {
                //当边框中数的和大于等于target,记录此时最小长度,
                // 并向右移动左边框减小范围
                minLen = Math.min(minLen, right - left);
                sum =sum-nums[left];
                left++;
            }
        }
        if (minLen==100001)//即所有数加起来也没有target大,返回0
            return 0;
        else return minLen;
    }
}

字符串的排列

给你两个字符串 s1s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false

换句话说,s1 的排列之一是 s2子串

示例 1:

输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

示例 2:

输入:s1= "ab" s2 = "eidboaoo"
输出:false

一看就是需要用滑动窗口的,用数组下标表示每个小写字母的ascii码,数组内存字母出现的次数。

public class 字符串的排列567 {
    //用滑动窗口写
    //突破点:s1,s2都为小写字母,可以用ascii码来表示,
    //所以可以创建长度为26的数组来存储值
    // 即97-‘a’到122- ‘a’就是从零开始的下标了
    
    public boolean checkInclusion(String s1, String s2) {
        if (s1.length() > s2.length()) return false;
        int[] a = new int[26];
        int[] b = new int[26];
        for (int i = 0; i < s1.length(); i++) {
            a[s1.charAt(i) - 'a']++;
        }//把s1中的值先存起来

        for (int i = 0; i < s1.length(); i++) {
            //存储s2的前s1长度的值
            b[s2.charAt(i) - 'a']++;
        }
        if(check(a, b)){//调方法判断是否相同,
            // 因为在下边循环就直接进入新一轮了
            return true;
        }
        for (int i=s1.length();i<s2.length();i++){
            b[s2.charAt(i)-'a']++;
            b[s2.charAt(i-s1.length())-'a']--;
            if(check(a, b)){
                return true;
            }
        }
        return false;
    }
    public boolean check(int[] a,int [] b){
        for (int i = 0; i <26 ; i++) {
            if(a[i]!=b[i])
                return false;
                 }
        return true;
            }
        }

进阶题目:无重复字符的最长子串:3

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

示例 1: 输入: “abcabcbb”

输出: 3

解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

示例 2:输入: “bbbbb”

输出: 1

解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

public class 无重复字符的最长子串3 {
    //创建集合存放窗口,
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int maxLength = 0;
        int left = 0;
        int right = 0;
        HashSet<Character> set = new HashSet<>();
        // set用于存储窗口内的字符,方便直接对元素查找
        while (right < n) {//遍历
            if (!set.contains(s.charAt(right))) {
                //判断窗口内是否含有字符s.charAt(right),如果不含有,则将该字符添加到set中,
                // 更新maxLength为当前窗口的最大长度。
                set.add(s.charAt(right));
                maxLength = Math.max(maxLength, right - left + 1);
                right++;
            } else {//含有字符s.charAt(right),收缩,
                set.remove(s.charAt(left));//移除,左边框,
                // 一直有就一直移除
                left++;
            }
        }     
        return maxLength;
    }
}

经典题目:最小覆盖子串:76

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

count=3;
以本例本方法来分析:
need[65]=1; need[66]=1;need[67]=1;
window[68]=1,w[o]=1;w[66]=1;w[69]=1;w[67]=1;
count=2;
length=6;

显然,也是一道滑动窗口的题目

但是小窗口中的值相对窗口是乱序的,就需要我们把每次窗口中的值用数组或者哈希表收集起来

package Train.滑动窗口;

public class 最小覆盖子串 {
        public String minWindow(String s, String t) {
            // 问题:如何从字符串中找到目标子串
            // 滑动窗口
            // 方法:整型数组存放 Char, Char 的 int 值范围为 0 ~ 127,因为只有英文字母
           
            if(s.length() == 0 || t.length() == 0 || s.length() < t.length()){
             return ""; // 如果字符串为空,或者长度小于需要匹配的长度
            }
            int[] need = new int[128];// 利用数组 need 存放匹配子串中需要的字符个数
            int[] window = new int[128];  // 利用数组 window 存放窗口中字符个数
            // 窗口中已经匹配的字符个数
            int count = 0;
            int left = 0;
            int right = 0;
            int minLength = s.length();
            String minString = "";
            // need 初始化,统计字符串t中每个字符的出现次数,并将统计结果保存在数组need中.
            for(int i = 0; i < t.length(); i ++ ){
                need[t.charAt(i)] ++;
            }
            while(right < s.length()){
                char ch = s.charAt(right);
                window[ch] ++;
                // 如果需要该字符,并且已有窗口内的字符个数 小于需要的字符个数
                if(need[ch] > 0 && need[ch] >= window[ch]){
                    count ++;
                }

                // 当需要的字符都已经包含在窗口中后,开始收缩 left
                while(count == t.length()){
                    ch = s.charAt(left);
                    // 当需要删除的字符,是必须留在窗口内时
                    if(need[ch] > 0 && need[ch] == window[ch]){
                        count --;
                    }
                    // 这边需要取 = ,因为可能一开始两个字符串就是匹配的,如 a , a return a
                    if(right - left + 1 <= minLength){
                        minLength = right - left + 1;
                        minString = s.substring(left, right + 1);
                    }
                    window[ch] --;
                    left ++;
                }
                right ++;
            }
            return minString;
        }
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值