每日力扣刷题day06(滑动窗口专项)

滑动窗口

滑动窗口算法是一种常用的算法技巧,适用于处理数组或字符串中的连续子序列问题。它通过动态调整窗口的大小和位置,能够在线性时间内解决许多复杂的子数组或子字符串问题。滑动窗口的核心思想是利用两个指针来表示窗口的左右边界,通过移动这两个指针来维护和调整窗口,使得窗口内的数据满足特定的条件。

滑动窗口的基本步骤

  1. 初始化:设定窗口的初始位置。通常情况下,左指针和右指针都从数组或字符串的起始位置开始。
  2. 扩展窗口:移动右指针以扩展窗口,直到窗口内的数据满足特定条件。
  3. 收缩窗口:在窗口满足条件的情况下,移动左指针以收缩窗口,同时检查并更新结果,以优化结果。
  4. 重复步骤2-3:直到右指针遍历完整个数组或字符串。

滑动窗口的两种常见类型

  1. 固定大小窗口:窗口大小固定,通过移动窗口的位置来检查每一个固定大小的子数组或子字符串。
  2. 可变大小窗口:窗口大小可变,通过调整左右指针的位置来动态改变窗口的大小,直到找到满足条件的最优解。

2024.5.27(3题、定长滑动窗口)

1456. 定长子串中元音的最大数目

题目链接

给你字符串 s 和整数 k

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

英文中的 元音字母 为(a, e, i, o, u)。

示例 1:

输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。

示例 2:

输入:s = "aeiou", k = 2
输出:2
解释:任意长度为 2 的子字符串都包含 2 个元音字母。

示例 3:

输入:s = "leetcode", k = 3
输出:2
解释:"lee"、"eet" 和 "ode" 都包含 2 个元音字母。

示例 4:

输入:s = "rhythms", k = 4
输出:0
解释:字符串 s 中不含任何元音字母。

示例 5:

输入:s = "tryhard", k = 4
输出:1

提示:

  • 1 <= s.length <= 10^5
  • s 由小写英文字母组成
  • 1 <= k <= s.length
解题思路
  1. 使用一个滑动窗口,窗口大小固定为 k
  2. 初始化窗口,计算第一个窗口内的元音字母数。
  3. 通过移动窗口(即右移左边界和右边界),更新窗口内的元音字母数,同时记录出现的最大元音字母数。
Java代码实现
class Solution {
    public int maxVowels(String s, int k) {
        // 用字符串表示元音字母集,便于查找
        String vowels = "aeiou";

        int maxVowels = 0;
        int currentVowels = 0;

        // 初始化窗口,计算第一个窗口内的元音字母数
        for (int i = 0; i < k; i++) {
            if (vowels.indexOf(s.charAt(i)) != -1) {
                currentVowels++;
            }
        }

        maxVowels = currentVowels;

        // 通过移动窗口更新元音字母数并记录最大值
        for (int i = k; i < s.length(); i++) {
            // 移除左边界元素
            if (vowels.indexOf(s.charAt(i - k)) != -1) {
                currentVowels--;
            }
            // 添加右边界元素
            if (vowels.indexOf(s.charAt(i)) != -1) {
                currentVowels++;
            }
            // 更新最大元音字母数
            maxVowels = Math.max(maxVowels, currentVowels);
        }

        return maxVowels;
    }
}
详细解释
  1. 定义元音字母集

    • 使用字符串存储元音字母(a, e, i, o, u),便于快速查找。
  2. 初始化窗口

    • 计算字符串 s 中前 k 个字符中的元音字母数,并将其存储在 currentVowels 中。
  3. 滑动窗口

    • 从索引 k 开始遍历字符串 s,每次向右移动窗口:
      • 如果窗口移除的左边界字符是元音字母,currentVowels 减一。
      • 如果窗口添加的右边界字符是元音字母,currentVowels 加一。
    • 每次移动后,更新 maxVowels,使其始终记录当前窗口中最多的元音字母数。
  4. 返回结果

    • 遍历结束后,返回 maxVowels,即为字符串 s 中长度为 k 的子字符串中可能包含的最大元音字母数。

这个方法的时间复杂度为 O(n),其中 n 是字符串 s 的长度,适合处理长度较大的字符串。

2269. 找到一个数字的 K 美丽值

题目链接

一个整数 numk 美丽值定义为 num 中符合以下条件的 子字符串 数目:

  • 子字符串长度为 k
  • 子字符串能整除 num

给你整数 numk ,请你返回 num 的 k 美丽值。

注意:

  • 允许有 前缀 0
  • 0 不能整除任何值。

一个 子字符串 是一个字符串里的连续一段字符序列。

示例 1:

输入:num = 240, k = 2
输出:2
解释:以下是 num 里长度为 k 的子字符串:
- "240" 中的 "24" :24 能整除 240 。
- "240" 中的 "40" :40 能整除 240 。
所以,k 美丽值为 2 。

示例 2:

输入:num = 430043, k = 2
输出:2
解释:以下是 num 里长度为 k 的子字符串:
- "430043" 中的 "43" :43 能整除 430043 。
- "430043" 中的 "30" :30 不能整除 430043 。
- "430043" 中的 "00" :0 不能整除 430043 。
- "430043" 中的 "04" :4 不能整除 430043 。
- "430043" 中的 "43" :43 能整除 430043 。
所以,k 美丽值为 2 。

提示:

  • 1 <= num <= 109
  • 1 <= k <= num.length (将 num 视为字符串)
解题思路
  1. 将整数 num 转换为字符串形式,方便提取子字符串。
  2. 使用滑动窗口方法,窗口大小固定为 k,从字符串的起始位置开始滑动。
  3. 对于每个窗口内的子字符串,检查其是否能整除 num
  4. 如果能整除,则计数器加一。
  5. 最终返回计数器的值。
Java代码实现
class Solution {
    public int divisorSubstrings(int num, int k) {
        String numStr = String.valueOf(num);
        int length = numStr.length();
        int kBeautyCount = 0;

        for (int i = 0; i <= length - k; i++) {
            String subStr = numStr.substring(i, i + k);
            int subNum = Integer.parseInt(subStr);

            // 排除子字符串为0的情况,因为0不能整除任何数
            if (subNum != 0 && num % subNum == 0) {
                kBeautyCount++;
            }
        }

        return kBeautyCount;
    }
}
详细解释
  1. 将整数转换为字符串

    • String numStr = String.valueOf(num);
    • 这样我们可以方便地获取 num 中的子字符串。
  2. 滑动窗口遍历

    • 使用一个 for 循环,遍历字符串 numStr,窗口大小为 k
    • for (int i = 0; i <= numStr.length() - k; i++) { ... }
    • 每次循环中,提取当前窗口的子字符串。
    • String subStr = numStr.substring(i, i + k);
  3. 检查子字符串是否能整除 num

    • 将子字符串转换为整数。
    • int subNum = Integer.parseInt(subStr);
    • 检查 subNum 是否不为 0 且能整除 num
    • if (subNum != 0 && num % subNum == 0) { kBeautyCount++; }
  4. 返回结果

    • 最终返回计数器 kBeautyCount,即为 numk 美丽值。

这个方法的时间复杂度为 O(n),其中 n 是字符串 num 的长度,因为我们需要遍历每个长度为 k 的子字符串并进行检查。空间复杂度为 O(1),因为我们只使用了常数级别的额外空间。

1984. 学生分数的最小差值

题目链接

给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k

从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分最低分差值 达到 最小化

返回可能的 最小差值

示例 1:

输入:nums = [90], k = 1
输出:0
解释:选出 1 名学生的分数,仅有 1 种方法:
- [90] 最高分和最低分之间的差值是 90 - 90 = 0
可能的最小差值是 0

示例 2:

输入:nums = [9,4,1,7], k = 2
输出:2
解释:选出 2 名学生的分数,有 6 种方法:
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 4 = 5
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 1 = 8
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 7 = 2
- [9,4,1,7] 最高分和最低分之间的差值是 4 - 1 = 3
- [9,4,1,7] 最高分和最低分之间的差值是 7 - 4 = 3
- [9,4,1,7] 最高分和最低分之间的差值是 7 - 1 = 6
可能的最小差值是 2

提示:

  • 1 <= k <= nums.length <= 1000
  • 0 <= nums[i] <= 105
解题思路
  1. 将数组 nums 排序。
  2. 使用滑动窗口遍历排序后的数组,对于每个窗口,计算最大值和最小值的差值。
  3. 记录并返回最小的差值。
Java代码实现
class Solution {
    public int minimumDifference(int[] nums, int k) {
        if (k == 1) {
            return 0; // 如果 k 为 1,差值肯定为 0
        }

        Arrays.sort(nums); // 将数组排序

        int minDifference = Integer.MAX_VALUE;

        // 使用滑动窗口遍历排序后的数组
        for (int i = 0; i <= nums.length - k; i++) {
            int currentDifference = nums[i + k - 1] - nums[i];
            minDifference = Math.min(minDifference, currentDifference);
        }

        return minDifference;
    }
}
详细解释
  1. 边界情况处理
    • 如果 k 为 1,那么差值肯定为 0,因为只有一个元素。
  2. 排序数组
    • 使用 Arrays.sort(nums) 对数组进行排序。排序后,相邻的元素差值最小。
  3. 滑动窗口
    • 遍历排序后的数组,使用一个大小为 k 的滑动窗口计算差值。窗口起始位置为 i,终止位置为 i + k - 1
    • int currentDifference = nums[i + k - 1] - nums[i]; 计算当前窗口内的最大值和最小值之间的差值。
    • 记录最小的差值:minDifference = Math.min(minDifference, currentDifference);
  4. 返回结果
    • 最终返回 minDifference,即为可能的最小差值。

这个算法的时间复杂度是 O(n log n),主要花费在数组的排序上。遍历数组的时间复杂度是 O(n),所以总体时间复杂度是 O(n log n)。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈ing小甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值