【算法系列篇】滑动窗口

news2024/11/26 13:55:30

在这里插入图片描述

文章目录

  • 前言
  • 什么是滑动窗口
  • 1.长度最小的子数组
    • 1.1 题目要求
    • 1.2 做题思路
  • 1.3 Java代码实现
  • 2.无重复字符的最长子串
    • 2.1 题目要求
    • 2.2 做题思路
    • 2.3 Java代码实现
  • 3.最大连续1的个数 III
    • 3.1 题目要求
    • 3.2 做题思路
    • 3.3 Java代码实现
  • 4.将x减到0的最小操作数
    • 4.1 题目要求
    • 4.2 做题思路
    • 4.3 Java代码实现
  • 5.水果成篮
    • 5.1 题目要求
    • 5.2 做题思路
    • 5.3 Java代码实现
  • 6.找到字符串中所有字母异位词
    • 6.1 题目要求
    • 6.2 做题思路
    • 6.3 Java代码实现
  • 7.串联所有单词的子串
    • 7.1 题目要求
    • 7.2 做题思路
    • 7.3 Java代码实现
  • 8.最小覆盖字串
    • 8.1 题目要求
    • 8.2 做题思路
    • 8.3 Java代码实现
  • 总结

前言

前面我们学习了什么是双指针,今天我将为大家分享一种特殊的双指针——滑动窗口,为什么说它是特殊的双指针呢?因为它也是有两个指针维护的,并且两个指针的移动方向是相同的,就跟我们平时生活中的窗口一样,两条边是往相同的方向移动的。

什么是滑动窗口

滑动窗口算法是一种用于解决数组或字符串相关问题的算法。它通过两个指针维护的一个窗口,在窗口内进行操作,并根据问题的要求移动窗口的起始位置或结束位置。

滑动窗口算法通常用于解决需要在连续子数组或子字符串中寻找最大值、最小值、满足特定条件的值等问题。它的基本思想是通过调整窗口的大小和位置,以便在不重复计算的情况下,高效地处理问题。

滑动窗口算法的步骤如下:

  1. 初始化窗口的起始位置和结束位置。
  2. 根据问题的要求,移动窗口的起始位置或结束位置。
  3. 在每次移动窗口时,根据窗口内的元素进行相应的操作,例如计算最大值、最小值、满足特定条件的值等。
  4. 重复步骤2和步骤3,直到窗口移动到数组或字符串的末尾。

滑动窗口算法的时间复杂度通常为O(n),其中n为数组或字符串的长度。它通过避免重复计算,提高了问题的解决效率。

我们通过几个例子来了解滑动窗口算法。

1.长度最小的子数组

https://leetcode.cn/problems/minimum-size-subarray-sum/

1.1 题目要求

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:

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

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
class Solution {
    public int minSubArrayLen(int target, int[] nums) {

    }
}

1.2 做题思路

我们先来看看,如果使用暴力解法该怎么做。分别以每个数字开始,向后找 和>=target的子数组。

在这里插入图片描述
暴力解法是通过两层循环,i 从 0开始,j 从 i+1 开始遍历整个数字,找到 和 >= target 的长度最小的子数组。并且这个和是每次循环都从 0 开始加的。

通过观察每次循环找到的长度最小的子数组,我们可以发现,left 指针和 right 指针都是向同一个方向向右移动的,暴力解法是每次循环结束之后 right 的位置就回到 left + 1 的位置,这样就多了很多重复的不必要的计算。所以滑动窗口就是在这个暴力解法的基础上进行优化的,每次 right 的指向不进行返回,而是继续向右滑动,那么为什么 right 的指向可以不用返回呢?因为每次循环 left 的指向都会向右移动一位,而你要想找的长度最小的子数组的和 >= target,你的 right 就必须也是向右移动或者不移动,但是绝对不会是向左移动(在数组元素都是正数的情况下),这种就是滑动窗口的基本特征

那么知道了使用滑动窗口这个算法的时候,我们来看看具体应该如何实现。定义一个 sum 变量,存储每次进窗口之后这个窗口元素的和,并且这个sum 不是每一次都是逐个累加而是用之前的 sum 加上进窗口的这个元素,当 sum >= target 的时候,就需要进行出窗口的操作,并且在出窗口的时候,还需要判断是否需要更新 ret 的值,继续寻找后面的长度最小的子数组,每出一个元素,sum 就需要减去这个出去的数,出窗口一直出到 sum < target,然后再进窗口,反复上面的操作,直到 right 到达数组末尾。

在这里插入图片描述

1.3 Java代码实现

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int ret = Integer.MAX_VALUE;
        for(int left = 0,right = 0, sum = 0; right < nums.length; right++) {
            sum += nums[right];
            while(sum >= target) {
                ret = Math.min(ret,right - left + 1);
                sum -= nums[left++];
            }
        }

        return ret == Integer.MAX_VALUE ? 0 : ret;
    }
}

在这里插入图片描述

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

https://leetcode.cn/problems/longest-substring-without-repeating-characters/

2.1 题目要求

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

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
class Solution {
    public int lengthOfLongestSubstring(String s) {

    }
}

2.2 做题思路

先来看看暴力解法如何解决
在这里插入图片描述
通过暴力解法,我们也可以发现,每次循环找到的无重复字符的最长子串它的 left 的 right 都是向右移动的,所以这个题目同样使用滑动窗口来解决。

因为这里求的是长度最长的无重复字符的子串,所以需要在进窗口的时候进行判断是否需要更新最大长度,而判断是否有重复的字符,我们可以借助哈希表来进行判断。

在这里插入图片描述

2.3 Java代码实现

class Solution {
    public int lengthOfLongestSubstring(String ss) {
        char[] s = ss.toCharArray();
        int[] hash = new int[128];
        int ret = 0;
        for(int left = 0, right = 0; right < s.length; right++) {
            char in = s[right];
            hash[in]++;
            if(hash[in] == 1) {
                ret = Math.max(ret,right - left + 1);
            }
            while(hash[in] > 1) {
                char out = s[left++];
                hash[out]--;
            }
        }

        return ret;
    }
}

在这里插入图片描述

3.最大连续1的个数 III

https://leetcode.cn/problems/max-consecutive-ones-iii/

3.1 题目要求

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。

示例 1:

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:

输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

提示:

1 <= nums.length <= 105
nums[i] 不是 0 就是 1
0 <= k <= nums.length
class Solution {
    public int longestOnes(int[] nums, int k) {
        
    }
}

3.2 做题思路

很多人看到这个翻转0不知道啥意思,说通俗点就是0可以变成1,同样,先来看看暴力解法如何解决。

在这里插入图片描述

left 指针和 right 指针都是向右方向移动的,所以这道题目同样使用滑动窗口来解决。我们的判断条件是以窗口内0的数量为判断条件,并且求的是最大长度,所以是在进窗口的时候进行判断。

在这里插入图片描述

3.3 Java代码实现

class Solution {
    public int longestOnes(int[] nums, int k) {
        int ret = 0;
        for(int left = 0, right = 0,count = 0; right < nums.length; right++) {
            if(nums[right] == 0) count++;
            if(count <= k) {
                ret = Math.max(ret,right - left + 1);
            }
            while(count > k) {
                if(nums[left++] == 0) count--;
            }
        }

        return ret;
    }
}

在这里插入图片描述

4.将x减到0的最小操作数

https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/

4.1 题目要求

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

示例 1:

输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。

示例 2:

输入:nums = [5,6,7,8,9], x = 4
输出:-1

示例 3:

输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。

提示:

1 <= nums.length <= 105
1 <= nums[i] <= 104
1 <= x <= 109
class Solution {
    public int minOperations(int[] nums, int x) {

    }
}

4.2 做题思路

这道题目的意思就是:给定一个数字x,每次从数组的最左边或者最右边选择一个数减去,然后使x减到0的最少的操作数。这道题目如果从正面去做的话,会先的非常麻烦,不妨我们换一条思路,他求的是数组两边减去的最少的数字,不妨我们就求数组中间最长的子数组,使它们的和为数组所有元素的总和减去 target ,这样就能是数组的两侧的和为 target 并且长度最短。所以这个题目就跟前面的求数组长度最长的子数组是差不多的,就只是判断的地方不同,这里需要在出窗口之后判断是否需要更新ret的值,这里我就不给大家过多介绍了,关键就是能够知道这个思路。

4.3 Java代码实现

class Solution {
    public int minOperations(int[] nums, int x) {
        int sum = 0;
        for(int n : nums) sum += n;  //求数组所有元素的总和
        int target = sum - x;
        if(target < 0) return -1;
        int ret = -1;
        for(int left = 0, right = 0,sum1 = 0; right < nums.length; right++) {
            sum1 += nums[right];
            while(sum1 > target) {
                sum1 -= nums[left++];
            }
            if(sum1 == target) {
                ret = Math.max(ret,right - left + 1);
            }
        }

        return ret == -1 ? -1 : nums.length - ret;
    }
}

在这里插入图片描述

5.水果成篮

https://leetcode.cn/problems/fruit-into-baskets/

5.1 题目要求

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

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

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当* 符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

提示:

1 <= fruits.length <= 105
0 <= fruits[i] < fruits.length
class Solution {
    public int totalFruit(int[] fruits) {

    }
}

5.2 做题思路

根据题目我们可以知道,只有两个篮子,只能装两种水果,并且每个篮子可以装的水果数是无限的,这道题跟子数组的问题是类似的,只是判断条件变成了水果的种类,进窗口的时候,添加水果的种类很简单,当这个种类的水果数量在之前为0时,就说明是一个新的种类,但是当我们出窗口的时候,如果某一种类的水果数量减为0的时候,就需要减去一种种类,那么我们如何统计某一水果的数量呢?哈希表,使用哈希表可以很高效的对某一种类的数量进行统计。

5.3 Java代码实现

class Solution {
    public int totalFruit(int[] fruits) {
        int ret = 1;
        int[] hash = new int[fruits.length];
        for(int left = 0, right = 0,kinds = 0; right < fruits.length; right++) {
            int in = fruits[right];
            if(hash[in]++ == 0) kinds++;
            if(kinds <= 2) {  //当水果的种类小于等于2的时候就需要做出判断
                ret = Math.max(ret,right - left + 1);
            }
            while(kinds > 2) {
                int out = fruits[left++];
                if(--hash[out] == 0) kinds--;
            }
        }

        return ret;
    }
}

在这里插入图片描述

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

https://leetcode.cn/problems/find-all-anagrams-in-a-string/

6.1 题目要求

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
class Solution {
    public List<Integer> findAnagrams(String s, String p) {

    }
}

6.2 做题思路

这道题其实就相当于使用一个大小为 p 字符串长度的框从 s 字符串头部开始框 p 字符串长度的字符串,然后判断框起来的字符串是否是 p 字符串的异位词。

窗口中的大小是确定的,最终的窗口的大小是p字符串的长度,也就是说我们的判断条件就是窗口内的字符串是否是p字符串的异位词,这也是解决这道题的关键。那么我们应该如何解决呢?同样是使用两个哈希表,第一个哈希表统计p字符串每个单词出现的次数,当进窗口的时候,第二个哈希表中该字符出现的次数就加一,并且如果将这个字符添加进哈希表之后,该哈希表中对应字符出现的次数小于等于第一个哈希表对应字符出现的次数,就说明进窗口的这个字符为有效字符,我们使用 count 来记录有效字符的个数,count++,当 count 等于 p 字符串的长度时,就需要更新结果:将 left 的坐标添加进列表中。

6.3 Java代码实现

class Solution {
    public List<Integer> findAnagrams(String ss, String pp) {
        List<Integer> list = new ArrayList<>();
        char[] s = ss.toCharArray();
        char[] p = pp.toCharArray();
        int[] hash1 = new int[26];
        int[] hash2 = new int[26];
        for(char ch : p) hash1[ch - 'a']++;
        int len = p.length;
        for(int left = 0, right = 0,count = 0; right < s.length; right++) {
            char in = s[right];
            if(++hash2[in - 'a'] <= hash1[in - 'a']) count++;
            if(right - left + 1 > len) {
                char out = s[left++];
                if(hash2[out - 'a']-- <= hash1[out - 'a']) count--;
            }
            if(count == len) {
                list.add(left);
            }
        }

        return list;
    }
}

在这里插入图片描述

7.串联所有单词的子串

https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

7.1 题目要求

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

  • 例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。
    返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 和 s 由小写英文字母组成
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {

    }
}

7.2 做题思路

这道题目跟上面的一个题目是类似的,只是这里将字母换成了单词,所以我们可以仿照上面一个题的思路,将words数组中的每个字符串当作一个字母,s 字符串每与words数组中每个单词相同长度的字符串作为一个字母。

在这里插入图片描述
因为不仅可以从 b 开始,将字符数组分为多个字符串,还可以从a和r开始,所以这里我们需要进行 words 数组中每个单词长度的循环次数的操作。

这里的哈希表用数组就不容易模拟了,所以这里就需要用到 HashMap

7.3 Java代码实现

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> list = new ArrayList<>();
        Map<String,Integer> map1 = new HashMap<>();
        for(String tmp : words) {
            map1.put(tmp,map1.getOrDefault(tmp,0) + 1);
        }
        int len = words.length;  //求words数组的大小
        int m = words[0].length();  //求words数组中每个单词的长度
        for(int i = 0; i < m; i++) {
            Map<String,Integer> map2 = new HashMap<>();
            for(int left = i, right = i, count = 0; right <= s.length()-m; right += m) {
                String in = s.substring(right,right + m);
                map2.put(in,map2.getOrDefault(in,0) + 1);
                if(map2.get(in) <= map1.getOrDefault(in,0)) count++;
                if(right - left + 1 > len * m) {
                    String out = s.substring(left,left + m);
                    if(map2.get(out) <= map1.getOrDefault(out,0)) count--;
                    map2.put(out,map2.get(out) - 1);
                    left += m;
                }
                if(count == len) list.add(left);
            }
        }

        return list;
    }
}

在这里插入图片描述

8.最小覆盖字串

https://leetcode.cn/problems/minimum-window-substring/

8.1 题目要求

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

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

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

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

m == s.length
n == t.length
1 <= m, n <= 105
s 和 t 由英文字母组成
class Solution {
    public String minWindow(String s, String t) {

    }
}

8.2 做题思路

因为这道题目中说了,可以出现重复的字符,所以这道题的判断条件就是窗口中是否含有 t 字符串中的所有字符,并且窗口中的每个字符出现的次数必须大于 t 字符串中每个字符出现的次数。那么这道题的步骤主要分为两步:

  1. 在s字符串中,找到含有 t 字符串中含有的所有字符的窗口
  2. 在这个窗口中找长度最小的子串

在这里插入图片描述

8.3 Java代码实现

class Solution {
    public String minWindow(String ss, String tt) {
        char[] s = ss.toCharArray();
        char[] t = tt.toCharArray();
        int[] hash1 = new int[128];
        int[] hash2 = new int[128];
        int kinds = 0;
        for(char ch : t) {
            if(hash1[ch]++ == 0) kinds++;
        }
        int begin = -1;
        int minLen = Integer.MAX_VALUE;
        for(int left = 0, right = 0,count = 0; right < s.length; right++) {
            char in = s[right];
            if(++hash2[in] == hash1[in]) count++;
            while(count == kinds) {
                if(right - left + 1 < minLen) {
                    begin = left;
                    minLen = right - left + 1;
                }
                char out = s[left++];
                if(hash2[out]-- == hash1[out]) count--;
            }
        }

        if(begin == -1) return new String();
        else return ss.substring(begin,begin + minLen);
    }
}

在这里插入图片描述

总结

当涉及到解决子串或子数组的问题时,滑动窗口算法是一种非常有效的技术。它通过维护一个窗口,该窗口在字符串或数组上滑动,以解决特定问题。

滑动窗口算法的主要思想是使用两个指针,一个指向窗口的起始位置,另一个指向窗口的结束位置。通过调整这两个指针,可以扩展或收缩窗口,以找到所需的解。算法的关键是确定窗口的移动规则以及如何在移动过程中更新解。

下面是滑动窗口算法的一般步骤:

  1. 初始化窗口的起始指针和结束指针,使其指向合理的位置。
  2. 进入一个循环,不断尝试扩展窗口,直到窗口无法再扩展为止。
  3. 在每次窗口移动时,更新解(如果有必要)。
  4. 如果窗口仍然满足问题的要求,尝试收缩窗口,以寻找更优的解。
  5. 重复步骤3和4,直到窗口完全收缩并处理完所有元素。

滑动窗口算法的优点是其时间复杂度通常是线性的,因为它避免了对每个子串或子数组的重复计算,节约了计算资源。它也通常可以在O(1)的空间复杂度下完成。

滑动窗口算法常用于解决一些经典问题,例如字符串匹配、子数组和子串求和、最长/最短子数组等。通过适当定义窗口的移动规则和解的更新方式,可以针对不同的具体问题进行定制化的实现。

总结而言,滑动窗口算法是一种高效的解决子串或子数组问题的方法。它的核心思想是通过维护一个窗口,在问题的限定条件下移动窗口的起始和结束指针,同时利用解的特性来优化计算过程。使用滑动窗口算法可以提高问题的解决效率,并减少不必要的计算。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/903608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu上使用osg3.2+osgearth2.9

一、介绍 在ubuntu上使用osgearth加载三维数字地球&#xff0c;首先要有osg和osgearth的库&#xff0c;这些可以直接使用apt-get下载安装&#xff0c;但是版本有些老&#xff0c;如果需要新版本的就需要自己编译。 #查看现有版本 sudo apt-cache madison openscenegraph #安装…

windows上ffmpeg如何录制双屏幕中的一个屏幕上的视频

首先&#xff0c;如何在window上安装ffmpeg自己查找scoop安装ffmpeg. 如题&#xff1a; 如果你有两个屏幕&#xff0c;如何让ffmpeg来录制其中的一个屏幕的视频呢。 很简单&#xff0c;首先你要查看另外一个屏幕的分辨率&#xff1a; 第一步&#xff1a;进入系统中 第二步&am…

VsCode报错:No such file or directory:‘文件名‘

1.问题&#xff1a; 昨天用VsCode直接打开py文件&#xff0c;运行后显示No such file or directory:‘directory’。但directory文件和该py文件在同一目录 2.原因&#xff1a; 直接打开py文件&#xff0c;Vscode看不到同一目录下的其他文件 3.解决方法&#xff1a; 打开文件夹…

漏洞指北-VulFocus靶场专栏-中级03

漏洞指北-VulFocus靶场专栏-初级03 中级009 &#x1f338;gxlcms-cve_2018_14685&#x1f338;step1&#xff1a;安装系统 密码rootstep2 进入后台页面 账号密码&#xff1a;admin amdin888step3 查看详细 有phpinfo() 中级010 &#x1f338;dedecms-cnvd_2018_01221&#x1f3…

EventBus3.0源码详解

详解之前要说明一下&#xff0c;LivedataBus 比EventBus更适合目前jetpack化的app&#xff0c;因为考虑到组件的生命周期处理&#xff0c;性能方面&#xff0c;EventBus还是要反射invoke的调用的&#xff0c;网上找不到有实测过的博文&#xff0c;我想来个性能对比实测&#xf…

【旅游度假】Axure酒店在线预订APP原型图 旅游度假子模块原型模板

作品概况 页面数量&#xff1a;共 10 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;旅游度假&#xff0c;生活服务 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本作品为「酒店在线预订」的移动端…

【淘宝】商品详情页+商品列表数据采集

作为国内最大的电商平台之一&#xff0c;淘宝数据采集具有多个维度。 有人需要采集商品信息&#xff0c;包括品类、品牌、产品名、价格、销量等字段&#xff0c;以了解商品销售状况、热门商品属性&#xff0c;进行市场扩大和重要决策&#xff1b; 有人需要采集产品评论&…

EndNote(二)【文献导入、全文检索】

在开始之前我们可以先分个组以管理自己的文件&#xff0c;如果不分组的话&#xff0c;它所有的文件都会放在unfiled这个目录下&#xff1a; 如何分组也很简单&#xff1a; 然后给个名字即可&#xff1a; 文献导入&#xff08;五种方法&#xff09; 第一种&#xff1a;pdf直接导…

STM32F4X USART串口使用

STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口&#xff0c;也是MCU在开发过程中的调试利器。串口通信有几个重要的参数&#xff0c;分别…

基于蚁狮算法优化的BP神经网络(预测应用) - 附代码

基于蚁狮算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于蚁狮算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.蚁狮优化BP神经网络2.1 BP神经网络参数设置2.2 蚁狮算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

Chrome如何安装插件(文件夹)

1.下载的插件 说明&#xff1a;插件文件夹 2.打开扩展程序位置 3.点击已加载的扩展程序 说明&#xff1a;找到插件的位置 4.报错 说明&#xff1a;那还要进入文件里面。 5.插件的位置 说明&#xff1a;如果已经安装了插件&#xff0c;那么需要查看插件的位置。chrome输入 …

todesk远程连接出现闪退如何解决

官方提供方案&#xff1a;ToDesk远程桌面软件-免费安全流畅的远程连接电脑手机https://www.todesk.com/faq/88.html 图1 打开上述网址&#xff0c;输入“闪退”进行搜索 图2 超级实用解决方法 &#xff08;本机软件设置一下即可&#xff09; 图3 其实图2就已经解决问题了&…

【汇编语言】CS、IP与代码段

文章目录 两个关键的寄存器在CS和IP指示下代码的运行问答 两个关键的寄存器 CS: 代码段寄存器&#xff1b;IP: 指令指针寄存器&#xff1b;CS:IP : CPU将内存中CS&#xff1a;IP只想的内容当作指令执行&#xff1b; 在CS和IP指示下代码的运行 具体步骤描述&#xff1a; 1、第…

【rust/egui】(四)看看template的app.rs:update以及组件TopBottomPanelButton

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 update update实际上还是eframe::App的…

利用快慢指针,求链表的中间结点,判断链表是否是环形链表

前言 &#xff08;1&#xff09;在学习数据结构链表部分的时候&#xff0c;老师给出了几个题目。其中两个题目采用了快慢指针的技术&#xff0c;感觉有意思&#xff0c;于是写一篇博客记录一下。 快慢指针 &#xff08;1&#xff09;我们先来介绍一下快慢指针技术。这个说起来其…

Java性能分析中常用命令和工具

当涉及到 Java 性能分析时&#xff0c;有一系列强大的命令和工具可以帮助开发人员分析应用程序的性能瓶颈、内存使用情况和线程问题。以下是一些常用的 Java 性能分析命令和工具&#xff0c;以及它们的详细说明和示例。 以下是一些常用的性能分析命令和工具汇总&#xff1a; …

vscode远程调试PHP代码

目录 一、准备工作 二、ssh连接和xdebug配置 1.ssh连接 2.xdebug配置 三、xdebug调试&#xff0c;访问 一、准备工作 1.安装vscode里面的两个扩展 2.安装对应PHP版本的xdebug 去xdebug官方&#xff0c;复制自己的phpinfo源码到方框里&#xff0c;再点击Analyse Xdebug: …

【⑫MySQL | 约束(二)】外键 | 默认值 | 检查约束 — 综合案例

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL外键 | 默认值 | 检查约束 以及综合案例的分享✨ 目录 前言6. 外键约束(FOREIGN KEY,FK)7. 默认值约束和检查约束8. 综合实战总结 6. 外键约束(FOREIGN KEY,FK) 前面介绍的完整性约束都是在单表中进行设置&…

VB6创建ActiveX exe简单方法

VB6创建的COM即可以是线程内的DLL&#xff0c;也可以是线程外独立的EXE&#xff0c;有些32位的旧东西做activex.exe封装后在新硬件新软件x64位上用还是可以的。其实activex DLL和activeX EXE主要功能并不需要改变。activex DLL可以是如下的样子&#xff1a; 一个主工程文件&…

​​Linux 信号量​​​​

信号量的产生原因 我们将可能会被多个执行流同时访问的资源叫做临界资源&#xff0c;临界资源需要进行保护否则会出现数据不一致等问题。 当我们仅用一个互斥锁对临界资源进行保护时&#xff0c;相当于我们将这块临界资源看作一个整体&#xff0c;同一时刻只允许一个执行流对…