算法沉淀——滑动窗口(leetcode真题剖析)

news2024/11/21 2:28:30

在这里插入图片描述

算法沉淀——滑动窗口

  • 01.长度最小的子数组
  • 02.无重复字符的最长子串
  • 03.最大连续1的个数 III
  • 04.将 x 减到 0 的最小操作数
  • 05.水果成篮
  • 06.找到字符串中所有字母异位词
  • 07.串联所有单词的子串
  • 08.最小覆盖子串

滑动窗口算法是一种用于解决数组或列表中子数组或子序列问题的有效技巧。它通过维护一个可变大小的窗口(通常是一个连续的子数组或子序列),在数据流中滑动该窗口来进行问题求解。这种方法在一维数组和二维数组中都有应用,并且在字符串处理中也很常见。

滑动窗口算法的基本思想是使用两个指针,通常是左指针(left)和右指针(right)来定义窗口,通过移动这两个指针,调整窗口的大小和位置,从而在不重复计算的情况下找到问题的解。

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

  1. 初始化窗口: 定义左指针和右指针,并将窗口初始化为满足问题条件的初始状态。
  2. 移动窗口: 不断移动右指针,扩展窗口大小,直到不再满足问题条件为止。
  3. 调整窗口: 一旦窗口不再满足问题条件,开始移动左指针,缩小窗口大小,直到满足问题条件为止。
  4. 记录解: 在窗口移动的过程中,记录满足问题条件的解。

滑动窗口算法适用于一些问题,例如:

  • 子数组和子序列的最大/最小值: 在数组中找到满足条件的子数组或子序列的最大或最小值。
  • 子数组和子序列的和或平均值: 在数组中找到满足条件的子数组或子序列的和或平均值。
  • 字符串处理问题: 如找到最小覆盖子串、找到没有重复字符的最长子串等。

下面我们通过几个真题来学习这个算法

01.长度最小的子数组

题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

给定一个含有 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

思路

在最开始没接触这种算法题时,我们要先建立滑动窗口的概念,首先像这种题目要求是连续的情况下,我们就要想到这种算法思想,然后滑动窗口主要分为四个步骤,进窗口、判断、出窗口、更新结果;其中更新结果要根据不同的情况放在不同的位置,比如上面这道题

这里使用滑动窗口算法,用于找到数组中和大于等于目标值 target 的最短子数组的长度。

  1. 初始化: 使用两个指针 leftright,都初始指向数组的起始位置。同时定义变量 sum 用来记录当前窗口中元素的和,以及变量 len 用来记录当前找到的最短子数组的长度。
  2. 窗口扩展: 在一个循环中,不断将右指针 right 向右移动,累加元素的值到 sum 中。如果 sum 大于等于目标值 target,说明当前窗口的和满足条件。
  3. 窗口收缩: 进入内层循环,将左指针 left 向右移动,缩小窗口大小,同时更新 len 为当前窗口的长度。然后从 sum 中减去左侧移出窗口的元素的值。
  4. 循环继续: 继续上述步骤,直到右指针 right 移动到数组的末尾。在整个过程中,不断更新 len 为找到的最短子数组的长度。
  5. 返回结果: 最终返回 len,如果没有找到满足条件的子数组,返回 0。

代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n=nums.size(),sum=0,len=INT_MAX;
        for(int left=0,right=0;right<n;right++){
            sum+=nums[right];
            while(sum>=target){
                len=min(len,right-left+1);
                sum-=nums[left++];
            }
        }
        return len==INT_MAX?0:len;
    }
};

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

题目链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/

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

示例 1:

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

示例 2:

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

示例 3:

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

思路

这里使用暴力枚举的方法是可以通过的,但是不推荐,这里我们使用滑动窗口来进行遍历,再借用一个哈希数组来记录字符出现的频次,每个字符最多出现一次,如果出现两次,那么左边一直向右滑动,直到变为1,再进行滑动时,我们更新最大长度,在使用哈希存储时,因为这里是字符,我们可以使用数组模拟哈希结构,这样可以提高效率。

  1. 初始化: 使用两个指针 leftright 分别指向字符串的起始位置,并初始化一个长度为 128 的哈希表 hash 用于记录字符出现的次数。同时,初始化变量 len 用于记录当前的最长无重复字符子串的长度。
  2. 窗口扩展: 在一个循环中,不断将右指针 right 向右移动,同时在哈希表中记录字符的出现次数。如果发现当前字符的出现次数大于 1,说明出现了重复字符。
  3. 窗口收缩: 进入内层循环,将左指针 left 向右移动,缩小窗口大小。在这个过程中,不断减少哈希表中左指针对应字符的出现次数,直到当前窗口中没有重复字符为止。
  4. 更新最长长度: 在每一步中,都更新 len 为当前窗口的长度 right - left + 1 与已经记录的最大长度的较大者。
  5. 循环继续: 继续上述步骤,直到右指针 right 移动到字符串的末尾。
  6. 返回结果: 最终返回 len,即最长无重复字符子串的长度。

代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128]={0};
        int n=s.size(),len=0,left=0,right=0;
        while(right<n){
            hash[s[right]]++;
            while(hash[s[right]]>1) hash[s[left++]]--;
            len = max(len,right-left+1);
            right++;
        }
        return len;
    }
};

03.最大连续1的个数 III

题目链接:https://leetcode.cn/problems/max-consecutive-ones-iii/

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 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。

思路

这里我们首先不要被题目误导,题目让我们翻转0,我们也不用真的翻转,我们可以利用滑动窗口的思想,在窗口内最大容纳k个0,超过就出窗口,直到又是k个0,再继续入窗口,每次更新最大长度即可。

  1. 初始化: 使用两个指针 leftright 分别指向数组的起始位置,并初始化变量 zero 用于记录当前窗口中 0 的个数。同时,初始化变量 len 用于记录当前的最大长度。
  2. 窗口扩展: 在一个循环中,不断将右指针 right 向右移动。如果当前元素为 0,将 zero 增加 1。
  3. 窗口收缩: 进入内层循环,如果窗口中的 0 的个数大于 k,说明需要缩小窗口,将左指针 left 向右移动,减少窗口中 0 的个数。在这个过程中,如果左指针指向的元素是 0,则将 zero 减少 1。
  4. 更新最大长度: 在每一步中,都更新 len 为当前窗口的长度 right - left + 1 与已经记录的最大长度的较大者。
  5. 循环继续: 继续上述步骤,直到右指针 right 移动到数组的末尾。
  6. 返回结果: 最终返回 len,即包含最多 k 个 0 的连续子数组的最大长度。

代码

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n=nums.size(),zero=0,len=0;
        for(int left=0,right=0;right<n;++right){
            if(nums[right]==0) zero++;
            while(zero>k) 
                if(nums[left++]==0) zero--;
            len=max(len,right-left+1);
        }
        return len;
    }
};

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

题目链接:https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/

给你一个整数数组 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 。

思路

首先我们看到这道题,可能并不容易想到用滑动窗口来解,可能有很多人开始会想到双指针,但是,那样要处理的细节太多,不容易通过,其实这里我们可以将问题转化,从左右两边减去最少的操作数来达到要求,我们可以转换成将数组总和减去X,在中间找到最长的子数组之和等于这个数,再将原数组长度与子数组长度相减便可以求出最小操作数,这样是不是就更简单了呢?

  1. 计算总和: 遍历数组,计算数组中所有元素的和,并保存在变量 sum 中。
  2. 计算目标差值: 计算目标差值 t,即 t = sum - x
  3. 滑动窗口求解: 使用滑动窗口的思想,在一个循环中,不断将右指针 right 向右移动,累加元素的值到 tmp 中。同时,在内层循环中,如果 tmp 大于目标差值 t,将左指针 left 向右移动,减少窗口的和。在每一步中,检查当前窗口的和是否等于目标差值 t
  4. 更新最大窗口长度: 在每一步中,如果当前窗口的和等于目标差值 t,则更新 ret 为当前窗口的长度 right - left + 1 和已经记录的最大长度的较大者。
  5. 计算结果: 最终返回操作数,即数组长度减去最大窗口长度。如果 ret 仍然为初始值 -1,则说明无法找到符合条件的子数组,返回 -1。

代码

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n=nums.size(),sum=0,ret=-1;
        for(const auto& num : nums) sum+=num;
        int t=sum-x;
        if(t<0) return -1;

        for(int left=0,right=0,tmp=0;right<n;++right){
            tmp+=nums[right];
            while(tmp>t) tmp-=nums[left++];
            if(tmp==t) ret=max(ret,right-left+1);
        }

        if(ret==-1) return -1;
        else return n-ret;
    }
};

05.水果成篮

题目链接:https://leetcode.cn/problems/fruit-into-baskets/

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 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] 这五棵树。

思路

首先我们这里同样可以采取滑动窗口的方式进出窗口,同时更新最大长度即可,这里需要借助哈希容器来记录水果的种类,当然,我们也可以直接用数组来模拟哈希,这样可以提高效率,这里题目告诉我们最大长度是10^5,因此我们可以建立一个100001大小的数组来模拟哈希。

  1. 初始化: 使用两个指针 leftright 分别指向数组的起始位置,并初始化一个数组 hash 用于记录水果的出现次数。同时,初始化变量 ret 用于记录最大长度,以及变量 kind 用于记录当前窗口中不同水果的种类数。
  2. 滑动窗口求解: 在一个循环中,不断将右指针 right 向右移动。如果当前水果是一种新的水果(即 hash[fruits[right]] == 0),则将 kind 增加 1。在哈希表中记录当前水果的出现次数。
  3. 窗口收缩: 如果当前窗口中不同水果的种类数 kind 大于 2,说明窗口中包含了超过两种不同水果。进入内层循环,将左指针 left 向右移动,减小窗口的大小。在这个过程中,不断减少哈希表中左指针指向的水果的出现次数,并更新 kind
  4. 更新最大长度: 在每一步中,都更新 ret 为当前窗口的长度 right - left + 1 与已经记录的最大长度的较大者。
  5. 循环继续: 继续上述步骤,直到右指针 right 移动到数组的末尾。
  6. 返回结果: 最终返回 ret,即包含最多两种不同水果的连续子数组的最大长度。

代码

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int hash[100001]={0};

        int ret=0,kind=0,n=fruits.size();
        for(int left=0,right=0;right<n;++right){
            if(hash[fruits[right]]==0) kind++;
            hash[fruits[right]]++;

            if(kind>2){
                hash[fruits[left]]--;
                if(hash[fruits[left]]==0) kind--;
                left++;
            }

            ret=max(ret,right-left+1);
        }
        return ret;
    }
};

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

题目链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/

给定两个字符串 sp,找到 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" 的异位词。

思路

这里我们可以借助两个哈希表,先存储p字符串的各字母个数,再使用另一个哈希表,进行长度为p字符串长度的固定滑动窗口对s字符串进行遍历,合法字符进窗口计数++,合法字符出窗口计数–,只有计数与p字符串长度相等时,存储窗口左下标,最后返回存储数组

代码

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;
        int hash1[26]={0};
        int hash2[26]={0};
        for(const auto& c:p) hash1[c-'a']++;

        int n=s.size(),m=p.size(),count=0;

        for(int left=0,right=0;right<n;++right){
            char c1=s[right];
            if(++hash2[c1-'a']<=hash1[c1-'a']) count++;
            if(right-left+1>m){
                char c2=s[left++];
                if(hash2[c2-'a']--<=hash1[c2-'a']) count--;
            }
            if(count==m) ret.push_back(left);
        }
        return ret;
    }
};

07.串联所有单词的子串

题目链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/

给定一个字符串 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. 初始化: 使用一个哈希表 hash1 记录单词列表 words 中每个单词的出现次数。
  2. 遍历起始位置: 对于每个可能的起始位置 i,其中 i 的范围是 [0, len - 1],其中 len 是单词长度。这是为了确保能够覆盖到所有可能的子串。
  3. 滑动窗口求解: 在一个循环中,不断将右指针 right 向右移动,每次移动一个单词的长度 len。在每一步中,截取字符串 str1,表示当前窗口的单词。然后,更新哈希表 hash2,表示当前窗口内各单词的出现次数。
  4. 检查匹配: 在每一步中,检查截取的字符串 str1 是否在哈希表 hash1 中出现,并且当前窗口内该单词的出现次数不超过 hash1 中的次数。如果是,则将 count 增加 1。这里和下面的count–,先检查是一个提升效率的方式,不写也不影响代码运行,主要原因是如果hash1中没有这个键,它就会直接插入,这对性能的消耗可能很大。
  5. 窗口收缩: 如果当前窗口的长度超过了需要的总长度 len * m,则需要收缩窗口。将左指针 left 向右移动,每次移动一个单词的长度 len。在每一步中,截取字符串 str2,表示当前窗口收缩的单词。然后,检查截取的字符串 str2 是否在哈希表 hash1 中出现,并且当前窗口内该单词的出现次数不超过 hash1 中的次数。如果是,则将 count 减少 1。
  6. 更新结果: 在每一步中,如果 count 等于单词列表 words 的长度 m,说明当前窗口中包含了所有的单词,将左指针的位置加入结果数组 ret
  7. 循环继续: 继续上述步骤,直到右指针 right 移动到字符串 s 的末尾。
  8. 返回结果: 最终返回结果数组 ret

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ret;
        unordered_map<string,int> hash1;
        for(const auto& str:words) hash1[str]++;

        int n=s.size(),m=words.size(),len=words[0].size();
        for(int i=0;i<len;++i){
            unordered_map<string,int> hash2;
            for(int left=i,right=i,count=0;right+len<=n;right+=len){
                string str1=s.substr(right,len);
                hash2[str1]++;;
                if(hash1.count(str1)&&hash2[str1]<=hash1[str1]) count++;

                if(right-left+1>len*m){
                    string str2=s.substr(left,len);
                    if(hash1.count(str2)&&hash2[str2]<=hash1[str2]) count--;
                    hash2[str2]--;
                    left+=len;
                }

                if(count==m) ret.push_back(left);
            }
        }
        return ret;
    }
};

08.最小覆盖子串

题目链接:https://leetcode.cn/problems/minimum-window-substring/

给你一个字符串 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。

思路

这里和前面两题类似,还是使用滑动窗口加哈希的方式来解决,只不过这里可以有重复字符,因此我们在计数时只要有一个,就不再计数,出窗口也一样,当计数和记录的种类相同时,我们更新起始位置和最小长度,最后返回字串即可。

  1. 初始化: 使用两个数组 hash1hash2 分别记录字符串 t 中字符的出现次数和当前窗口中字符的出现次数。同时,使用变量 kind 记录不同字符的种类数。
  2. 统计字符串 t 中字符次数: 遍历字符串 t,更新数组 hash1 中对应字符的出现次数,并更新 kind
  3. 滑动窗口求解: 在一个循环中,不断将右指针 right 向右移动。在每一步中,截取字符串 c1 表示当前窗口的右端字符。然后,更新数组 hash2 中右指针指向字符的出现次数,并根据当前窗口中字符的出现次数与字符串 t 中相应字符的出现次数比较,更新 count
  4. 检查匹配: 在每一步中,如果 count 等于 kind,说明当前窗口中包含了字符串 t 中的所有字符。进入内层循环,更新最小窗口的长度和起始位置。将左指针 left 向右移动,减小窗口的大小。在这个过程中,不断减少数组 hash2 中左指针指向字符的出现次数,并根据当前窗口中字符的出现次数与字符串 t 中相应字符的出现次数比较,更新 count
  5. 更新结果: 在每一步中,如果找到更小的窗口,则更新 minlbegin
  6. 循环继续: 继续上述步骤,直到右指针 right 移动到字符串 s 的末尾。
  7. 返回结果: 最终返回最小窗口子串,如果没有找到则返回空字符串。

代码

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128]={0};
        int hash2[128]={0};
        int kind=0;
        for(const auto& ch:t) 
            if(hash1[ch]++==0) kind++;
        
        int begin=-1,minl=INT_MAX,n=s.size();
        for(int left=0,right=0,count=0;right<n;++right){
            char c1=s[right];
            if(++hash2[c1]==hash1[c1]) count++;
            while(count==kind){
                if(right-left+1<minl){
                    minl=right-left+1;
                    begin=left;
                }
                char c2=s[left++];
                if(hash2[c2]--==hash1[c2]) count--;
            }
        }

        if(begin==-1) return "";
        return s.substr(begin,minl);
    }
};

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

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

相关文章

不确定优化入门:用简单实例讲明白随机规划、鲁棒优化和分布鲁棒优化

文章目录 1 引言2 学习动机3 经典问题4 解决方案4.1 忽略不确定性4.2 随机规划4.3 鲁棒优化4.4 分布鲁棒优化 5 总结相关阅读 1 引言 按2024的原定计划&#xff0c;今年开始要学习不确定优化了。 粗略翻阅了一些相关的书籍和教程&#xff0c;大都包含许多数学公式&#xff0c…

xxl-job相关面试题整理

什么是xxl-job&#xff1f; ​ xxl-job是一个分布式的任务调度平台&#xff0c;其核心设计目标是&#xff1a;学习简单、开发迅速、轻量级、易扩展&#xff0c;现在已经开放源代码并接入多家公司的线上产品线&#xff0c;开箱即用。xxl是xxl-job的开发者大众点评的许雪里名称的…

腾讯云幻兽帕鲁4核16G14M服务器性能测评和价格

腾讯云幻兽帕鲁服务器4核16G14M配置&#xff0c;14M公网带宽&#xff0c;限制2500GB月流量&#xff0c;系统盘为220GB SSD盘&#xff0c;优惠价格66元1个月&#xff0c;277元3个月&#xff0c;支持4到8个玩家畅玩&#xff0c;地域可选择上海/北京/成都/南京/广州&#xff0c;腾…

在windows环境下安装hadoop

Hadoop是一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下&#xff0c;开发分布式程序。但这个架构是基于java语言开发的&#xff0c;所以要先进行jdk的安装&#xff0c;如果电脑已经配置过jdk或者是曾经运行成功过java文件&#xff0c;那就可以跳过第一步。 …

单入双出高电压信号隔离变送器

定义&#xff1a;一路高电压信号输入&#xff0c;双路国际标准模拟量信号输出的小型仪器设备。 单入双出高电压模拟量信号隔离变送器 型号&#xff1a;JSD TAH-1002 特征&#xff1a; ◆薄体积&#xff0c;低成本&#xff0c;国际标准DIN35mm导轨安装方式 ◆五端隔离(输入、…

实验2:DEBUG基本命令使用

目录 1、实验目的&#xff1a; 2、实验内容&#xff1a; 3、实验要求&#xff1a; 4、源代码&#xff1a; 5、实验结果 1、实验目的&#xff1a; 熟悉汇编语言程序设计的上机过程&#xff0c;掌握DEBUG的基本命令和功能。 2、实验内容&#xff1a; 从键盘键入一个大写英…

解锁多模态独特魅力-“机器人+Agent+多传感器融合+3DLLM”诠释终极组合大招!

01-Multiply算法背景 01.01-触觉传感器 触觉传感器是一种用于感知和测量物体接触力、形状、纹理和其他相关参数的传感器。它们模拟人类触觉系统&#xff0c;通过收集和解释物体与传感器之间的相互作用来获取信息。工作原理&#xff1a;触觉传感器使用不同的原理来感知接触力和…

Spring 事务原理二

该说些什么呢&#xff1f;一连几天&#xff0c;我都沉溺在孤芳自赏的思维中无法自拔。不知道自己为什么会有这种令人不齿的表现&#xff0c;更不知道这颗定时炸弹何时会将人炸的粉身碎骨。好在儒派宗师曾老夫子“吾日三省吾身”的名言警醒了我。遂潜心自省&#xff0c;溯源头以…

springIoc以及注解的使用

注解 注解的定义 注解&#xff08;Annotation&#xff09;是一种在 Java 程序中以元数据的形式对代码进行标记和说明的机制。它可以被添加到类、方法、字段、参数等程序元素上&#xff0c;用于提供额外的信息和指示。 也就是说注解是一种标记 注解怎么生效呢&#xff1f; 通…

Element-Plus如何实现表单校验和表单重置

一&#xff1a;页面布局介绍&#xff1a; 这是我刚刚用基于vue3element-plus写好的一个部门管理的页面 基本的增删改查已经写好&#xff0c;下面我只提供页面的template和style的代码&#xff1a; template <template><el-card class"box-card"><…

静态代理IP该如何助力Facebook多账号注册运营?

在Facebook运营中&#xff0c;充分利用静态代理IP是多账号运营的关键一环。通过合理运用静态代理IP&#xff0c;不仅可以提高账号安全性&#xff0c;还能有效应对Facebook的算法和限制。以下是这些关键点&#xff0c;可以帮助你了解如何运用静态代理IP进行Facebook多账号运营&a…

BGP:04 fake-as

使用 fake-as 可以将本地真实的 AS 编号隐藏&#xff0c;其他 AS 内的对等体在指定本端对等体所在的AS 编号时&#xff0c;应该设置成这个伪AS 编号。 这是实验拓扑&#xff0c;IBGP EBGP 邻居都使用物理接口来建立 基本配置&#xff1a; R1: sys sysname R1 int loo0 ip add…

网络原理,网络通信以及网络协议

​​​​&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录专栏&#xff1a;网络原理,网络通信以及网络协议 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 网络原理概念网络通信局域网LAN广域网WAN 网络通信IP地址端口号…

第17节-高质量简历写作求职通关-投递反馈

&#xff08;点击即可收听&#xff09; 投递跟进和感谢信 如果对一家公司特别心仪&#xff0c;但是投递简历后一直得不到回复怎么办&#xff1f; 面试之后觉得自己没有表现好怎么办&#xff1f; 面试完几天了&#xff0c;依然没有得到回应怎么办&#xff1f; 这个时候你需要写一…

OkHttp完全解读

一&#xff0c;概述 OkHttp作为android非常流行的网络框架&#xff0c;笔者认为有必要剖析此框架实现原理&#xff0c;抽取并理解此框架优秀的设计模式。OkHttp有几个重要的作用&#xff0c;如桥接、缓存、连接复用等&#xff0c;本文笔者将从使用出发&#xff0c;解读源码&am…

sqli-labs靶场第七关

7、第七关 id1 --单引号报错,id1" --双引号不报错,可以判断是单引号闭合 id1) --也报错&#xff0c;尝试两个括号闭合&#xff0c;id1)) --不报错 接下来用脚本爆库 import stringimport requestsnumbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] letters2 list(string.ascii_…

二、Gradle 与 Idea 整合

这里写自定义目录标题 1、Groovy简介2、Groovy 安装3、创建 Groovy 项目4、Groovy 基本语法 1、Groovy简介 详细了解请参考&#xff1a;http://www.groovy-lang.org/documentation.html 2、Groovy 安装 下载后解压到本地 验证&#xff1a; groovy的安装情况 3、创建 Groo…

231. Power of Two(2 的幂)

题目描述 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2 x n 2^x n2x&#xff0c;则认为 n 是 2 的幂次方。 问题分析 题目要求的是给定一个数判断…

[ESP32 IDF] wifi 的应用

目录 背景知识 wifi的基本连接使用 WiFi篇—— WiFi两种模式文章中二、WiFi 的启动&#xff08;STA 及 AP 模式&#xff09; 输出现象 通过websocket控制LED 实践验证 实验现象 背景知识 WIFI是ESP32非常重要的一个功能&#xff0c;想要使用一下IDF的API实现将ESP32连…

Golang Playground: 轻松提升你的技能

探索、实验和学习 Go 语言 Golang Playground 是一个在线工具&#xff0c;允许用户在方便且友好的环境中实验、练习和提升他们的编码技能。无论是初学者还是开发人员&#xff0c;Golang Playground 都提供了一个无需本地安装的环境&#xff0c;可以轻松编写、编译和执行 Go 代…