【C++】滑动窗口算法习题
🎆个人主页:夜晚中的人海
今日语录:人生就是这样,要耐的住寂寞,才守得住繁华
文章目录
- 🚀一、长度最小的子数组
- 🎉二、无重复字符的最长子串
- 🚘三、最大连续1的个数 III
- 🎡四、将x减到0的最小操作数
- 🏝️五、找到字符中所有字母的异位词
- ⭐六、串联所有单词的子串
🚀一、长度最小的子数组
题目链接:长度最小的子数组
题目描述:
解题思路:
1.暴力枚举,枚举任意一个数字当作起始位置,然后从这个位置开始寻找一段最短区间满足 >= target(注:这方法会超时,效率低)
2.滑动窗口,由于题目要的是一段连续的区间,因此我们可以采用滑动窗口的办法。使用两个指针left和right同时指向起始位置,在right小于数组长度前提下,不断向右移动进行累加操作(进窗口)直到它 >= target(判断条件),记录该段区间的长度(更新结果),然后将左端元素划出去(出窗口)同时并判断是否满足条件,如果不满足,则让right++ (进入下一个窗口)
代码实现:
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int ret = INT_MAX,sum = 0;for(int left = 0,right = 0;right < nums.size();right++){sum += nums[right];while(sum >= target){//更新结果ret = min(ret,right - left + 1);sum -= nums[left++];}}return ret == INT_MAX ? 0 : ret;}
};
🎉二、无重复字符的最长子串
题目链接:无重复字符的最长子串
题目描述:
解题思路:
1.暴力枚举,从每一个位置开始向后,看看无重复字符在什么位置,返回长度最长的那个(注:效率低)
2.滑动窗口 + 哈希表,题目要求依旧是一段连续的区间,因此可以采用滑动窗口的办法。定义两个指针left 和 right,让右端元素right进入窗口(进窗口),并用哈希表统计该字符的频次,如果该字符 > 1(判断条件),则从左侧开始滑出窗口(出窗口),直到该字符的频次为1时,更新结果
代码实现:
class Solution {
public:int lengthOfLongestSubstring(string s) {int hash[128] = {0};int n = s.size();int ret = 0; for(int left = 0,right = 0;right < n;right++){hash[s[right]]++;while(hash[s[right]] > 1){hash[s[left++]]--;}ret = max(ret,right - left + 1);}return ret;}
};
🚘三、最大连续1的个数 III
题目链接:最大连续1的个数 III
题目描述:
解题思路:
1.因为该题的要求依旧是一段连续的空间,因此我们可以采用滑动窗口的方法来解决。
2.我们不要想着如何去翻转,把问题复杂化。它的核心就是0的个数不超过k个,我们只要解决这一问题即可
3.可以使用一个变量zero来记录0的个数,用两个指针left和right,right指针负责进窗口,当遇到0时让zero++,直到当zero > k时(判断条件),判断left所指元素是否为0进行出窗口,最后更新结果
代码实现:
class Solution {
public:int longestOnes(vector<int>& nums, int k) {int n = nums.size();int len = 0;for(int left = 0,right= 0,zero = 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;}
};
🎡四、将x减到0的最小操作数
题目链接:将x减到0的最小操作数
题目描述:
解题思路:
由于题目要求的是减去数组左或右两端连续的和为x的最短数组,如果按照题目的要求那我们解决这个问题就比较棘手,由于我们不知道它是减去左边的还是减去右边的,或者连续减去左边等情况,因此我们可以将其进行转化为数组内一段连续的和为sum(nums) - x的最长数组,使用滑动窗口的解法,然后用整个数组的大小减去该段最长数组的大小,我们就得到了题目要求的最短操作数了
代码实现:
class Solution {
public:int minOperations(vector<int>& nums, int x) {int sum = 0;for(auto n : nums){sum += n;}int ret = -1;int target = sum - x;if(target < 0){return -1;}for(int left = 0,right = 0,tmp = 0;right < nums.size();right++){tmp += nums[right];while(tmp > target){tmp -= nums[left++];}if(tmp == target){ret = max(ret,right - left + 1);}}if(ret == -1)return ret;elsereturn nums.size() - ret;}
};
🏝️五、找到字符中所有字母的异位词
题目链接:找到字符中所有字母的异位词
题目描述:
解题思路:
滑动窗口+ 哈希表,由题可知,字符串p的异位词的长度⼀定与字符串p的长度相同,所以可以在字符串s 中构造⼀个长度为字符串p的长度相同的滑动窗口,用哈希表记录字符串p中字符出现的个数,用一个变量count记录长度,不断进窗口,如果大于异位词的长度并且出现的字符在字符串p中也有(判断条件),就出窗口,让count–,相反就让count++,如果等于字符串p的长度就更新结果
代码实现:
class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> ret;int hash1[26] = {0};int n = s.size();int m = p.size();for(auto ch : p){hash1[ch - 'a']++;}int hash2[26] = {0};int count = 0;for(int left = 0,right = 0;right < n;right++){char in = s[right];if(++hash2[in - 'a'] <= hash1[in - 'a']){count++;}if(right - left + 1 > m){ char out = s[left++];if(hash2[out - 'a']-- <= hash1[out - 'a']){count--;}}if(count == m){ret.push_back(left);}}return ret; }
};
⭐六、串联所有单词的子串
题目链接:串联所有单词的子串
题目描述:
解题思路:
这道题的解法与上道题的异位词解法类似,无非就是把字母转化为一个单词,因此同样采用哈希 + 滑动窗口的解法
代码实现:
class Solution {
public:vector<int> findSubstring(string s, vector<string>& words) {vector<int> ret;unordered_map<string ,int> hash1;for(auto& e:words){hash1[e]++;}int len = words[0].size();int m = words.size();for(int i = 0;i < len;i++){unordered_map<string,int> hash2;for(int left = i,right = i,count = 0;right + len <= s.size();right += len){string in = s.substr(right,len);hash2[in]++;if(hash2[in] <= hash1[in]){count++;}if(right - left + 1 > len * m){string out = s.substr(left,len);if(hash2[out] <= hash1[out]){count--;}hash2[out]--;left += len;}if(count == m){ret.push_back(left);}}}return ret;