滑动窗口0x3f刷题

20250426

我正学着如何爱你

果然多做几遍我就会了,果然多复习几次我就会了

  • 按照专题刷题,而不是随机刷题。同一个专题下的题目,套路是一样的,刷题效率杠杠滴~
  • 螺旋上升式学习:先完成 1700 难度分以下的题目。把各个知识点的基础题刷一遍,再刷更难的题目。

滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)

定长滑窗

入:下标为 i 的元素进入窗口,更新相关统计量。如果 i<k−1 则重复第一步。
更新:更新答案。一般是更新最大值/最小值。
出:下标为 i−k+1 的元素离开窗口,更新相关统计量。

索引 i 表示当前遍历到的字符串位置。
窗口的右边界是 i。
窗口的大小为 k,。
窗口的左边界是 i - k + 1。
因此窗口的范围是 [i - k + 1, i](闭区间)

请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。

class Solution {
public:
    int maxVowels(string s, int k) {
        int ans = 0,vowel = 0;
        for(int i = 0;i < s.length();i++){
            if(s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u')
            vowel++;
            if(i < k-1)continue;
            ans = max(ans,vowel);
            char out = s[i-k+1];
            if(out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u')
            vowel--;
        }
        return ans;
    }
};

给出一个由 n 个元素组成的整数数组 nums 和一个整数 k 。

请找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        int n = nums.size();
        double sum = 0,ans = INT_MIN;
        for(int i = 0;i < n;i++){
            sum+=nums[i];
            if(i < k - 1)continue;
            ans = max(ans,sum/k);
            sum-=nums[i-k+1];
        }
        return ans;
    }
};
  • 求最大值,则令max=INT_MIN;来赋初值

给你一个下标从 0 开始的数组 nums ,数组中有 n 个整数,另给你一个整数 k 。

半径为 k 的子数组平均值 是指:nums 中一个以下标 i 为 中心 且 半径 为 k 的子数组中所有元素的平均值,即下标在 i - k 和 i + k 范围(含 i - k 和 i + k)内所有元素的平均值。如果在下标 i 前或后不足 k 个元素,那么 半径为 k 的子数组平均值 是 -1 。
构建并返回一个长度为 n 的数组 avgs ,其中 avgs[i] 是以下标 i 为中心的子数组的 半径为 k 的子数组平均值 。

class Solution {
public:
    vector<int> getAverages(vector<int>& nums, int k) {
        long long sum = 0;
        int n = nums.size();
        vector<int> ans(n,-1);
        for(int right = 0;right < n;right++){
            sum += nums[right];//入
            if(right<(2*k))continue;//定长窗口
            ans[right-k]=sum/(2*k+1);//更新(right-k即为i)
            sum-=nums[right-2*k];//出
        }
        return ans;
    }
};
  • 在下标 i 前或后不足 k 个元素时需要输出的是-1,所以可以直接先定义一个值为-1的数组再进行赋值
  • 明确了定长为2k+1,再明确需要出的和入的对应数字即可

给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[i] 要么是 'W' 要么是 'B' ,表示第 i 块的颜色。字符 'W' 和 'B' 分别表示白色和黑色。

给你一个整数 k ,表示想要 连续 黑色块的数目。
每一次操作中,你可以选择一个白色块将它 涂成 黑色块。
请你返回至少出现 一次 连续 k 个黑色块的 最少 操作次数。

  • 解法一:
class Solution {
public:
    // 转换为求:长度为K的窗口中,黑色块最多的个数
    int minimumRecolors(string blocks, int k) {
        const char BLACK = 'B';
        int count = 0;
        int maxCount = 0;
        for (int i = 0; i < blocks.length(); i++) {
            if (blocks[i] == BLACK) {count++;} //入
            if (i < k - 1) {continue;}// 定长窗口
            maxCount = max(maxCount, count);//更新最值
            if (blocks[i - k + 1] == BLACK) {count--;}//出
        }
        return k - maxCount;
    }
};
  • 要使得操作次数最少,则需要定长窗口里的白色最少,即黑色最多,所以计算定长里black最多的cnt再用k减掉就得到了答案

  • 解法二:

class Solution {
public:
    int minimumRecolors(string blocks, int k) {
        int cnt_w = 0;
        for (int i = 0; i < k; i++) {
            cnt_w += blocks[i] & 1;
        }
        int ans = cnt_w;
        for (int i = k; i < blocks.length(); i++) {
            cnt_w += (blocks[i] & 1) - (blocks[i - k] & 1);
            ans = min(ans, cnt_w);
        }
        return ans;
    }
};
  • 使用位运算 (blocks[i] & 1) 判断字符是否为 'W':
    • 如果字符是 'W',其 ASCII 值为 87,二进制最低位为 1,因此 (blocks[i] & 1) 结果为 1。
    • 如果字符是 'B',其 ASCII 值为 66,二进制最低位为 0,因此 (blocks[i] & 1) 结果为 0。
    • 把 if-else(或者 ?: 三目运算符)写成 s[i] & 1 就会快很多(避免分支预测)

20250427

给你一个整数数组 nums 和两个正整数 m 和 k 。

请你返回 nums 中长度为 k 的 几乎唯一 子数组的 最大和 ,如果不存在几乎唯一子数组,请你返回 0 。
如果 nums 的一个子数组有至少 m 个互不相同的元素,我们称它是 几乎唯一 子数组。
子数组指的是一个数组中一段连续 非空 的元素序列。

class Solution {
public:
    long long maxSum(vector<int>& nums, int m, int k) {
        long long ans = 0,s = 0;
        unordered_map<int, int> cnt;
        for(int i = 0;i < nums.size();i++){
            s += nums[i];
            cnt[nums[i]]++;
  
            int left = i - k + 1;
            if(left < 0)continue;//定长窗口
            if(cnt.size() >= m) ans = max(ans, s);//如果满足条件就更新最值

            int out = nums[left];
            s -= out;
            if(--cnt[out] == 0)cnt.erase(out);
        
        return ans;
    }
};
  • 哈希表用于记录出现次数
    • 哈希表即键值对命名键,递增值
    • 用于插删查
    • 声明:
      • `std::unordered_map<KeyType, ValueType> mapName;
      • KeyType:键的类型。
      • ValueType:值的类型。
      • mapName:哈希表的名称`
      • eg. unordered_map<int, int> cnt;
    • 插:
      • mapName[key] = value;
        • 如果键 key 不存在,则插入新的键值对。
        • 如果键 key 已存在,则更新对应的值。
    • 查:
      • mapName.find(key)
        • 如果找到键 key,返回一个指向该键值对的迭代器。
        • 如果未找到键 key,返回 mapName.end(),表示哈希表的末尾。
    • 删:
      • mapName.erase(key);
    • 遍历:

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和:

  • 子数组的长度是 k,且
  • 子数组中的所有元素 各不相同 。
    返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0 。
    子数组 是数组中一段连续非空的元素序列。
class Solution {
public:
    long long maximumSubarraySum(vector<int>& nums, int k) {
        long long ans = 0,s = 0;
        unordered_map<int, int> cnt;
        for(int i = 0;i < nums.size();i++){
            s += nums[i];
            cnt[nums[i]]++;

            int left =  i - k + 1;
            if(left < 0)continue;
            if(cnt.size() == k)
            ans = max(ans, s);
            
            int out = nums[left];
            s -= out;
            if(--cnt[out] == 0)
            cnt.erase(out);
        }
        return ans;
    }
};
  • 同上题题意相似, ≥m 改成 =k 即可。
posted @ 2025-04-27 00:38  0216mcdonalds  阅读(45)  评论(0)    收藏  举报