定长滑动窗口


定长滑动窗口

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 个元音字母

思路

对于下图的字符串 abci,假如我们已经计算出了子串 abc 的元音个数,那么从子串 abc 到子串 bci,只需要考虑移除(离开窗口)的字母 a 是不是元音,以及添加(进入窗口)的字母 i 是不是元音即可,因为中间的字母 b 和 c 都在这两个子串中。


示例 1,s=abciiidef, k=3。

从左到右遍历 s。
首先统计前 k−1=2 个字母的元音个数,这有 1 个。
s[2]=c 进入窗口,此时找到了第一个长为 k 的子串 abc,现在元音个数有 1 个,更新答案最大值。然后 s[0]=a 离开窗口,现在元音个数有 0 个。
s[3]=i 进入窗口,此时找到了第二个长为 k 的子串 bci,现在元音个数有 1 个,更新答案最大值。然后 s[1]=b 离开窗口,现在元音个数有 1 个。
s[4]=i 进入窗口,此时找到了第三个长为 k 的子串 cii,现在元音个数有 2 个,更新答案最大值。然后 s[2]=c 离开窗口,现在元音个数有 2 个。
s[5]=i 进入窗口,此时找到了第四个长为 k 的子串 iii,现在元音个数有 3 个,更新答案最大值。然后 s[3]=i 离开窗口,现在元音个数有 2 个。
s[6]=d 进入窗口,此时找到了第五个长为 k 的子串 iid,现在元音个数有 2 个,更新答案最大值。然后 s[4]=i 离开窗口,现在元音个数有 1 个。
s[7]=e 进入窗口,此时找到了第六个长为 k 的子串 ide,现在元音个数有 2 个,更新答案最大值。然后 s[5]=i 离开窗口,现在元音个数有 1 个。
s[8]=f 进入窗口,此时找到了第七个长为 k 的子串 def,现在元音个数有 1 个,更新答案最大值。遍历结束。

因此,可以不断循环递增窗口右端点i,并判断新增的端点是否为元音字母,如果是则答案加1
之后判断现在是否形成窗口,窗口左端点下标为i-k+1(k为窗口长度),若i-k+1<0则continue,>0则用max记录新答案
之后窗口整体右移一位,因此要判断窗口左端点是否为元音字母,若是则答案减1

class Solution {
    public int maxVowels(String s, int k) {
        int len = s.length();
        int preans = 0 , ans = 0;
        //枚举窗口右端点
        for(int i=0;i<len;i++){
            //1.右端点进入窗口
            char ch = s.charAt(i);
            if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u'){
                preans++;
            }
            //窗口左端点小于0,说明尚未形成第一个窗口
            if(i-k+1<0)continue;
            //答案已经等于理论最大值,无需再循环
            if(preans == k)return k;
            //2.更新答案
            ans = Math.max(preans,ans);
            //3.左端点离开窗口,为下一个循环做准备
            char chl = s.charAt(i-k+1);
            if(chl == 'a' || chl == 'e' || chl == 'i' || chl == 'o' || chl == 'u'){
                preans--;
            }
        }
        return ans;
    }
}

注:也可以先把s变为字符数组 char[] arr = s.toCharArray();,这样就不用charAt了

643. 子数组最大平均数 I

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

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

任何误差小于 10-5 的答案都将被视为正确答案。

示例 1:

输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
示例 2:

输入:nums = [5], k = 1
输出:5.00000

提示:

  • n == nums.length
  • 1 <= k <= n <= 1e5
  • -1e4 <= nums[i] <= 1e4
class Solution {
    public double findMaxAverage(int[] nums, int k) {
        double sum = 0 , ans = -1e9;//因为数组中可能会有负数
        for(int i=0;i<nums.length;i++){
            sum += nums[i];
            if(i-k+1<0)continue;
            ans = Math.max(ans,sum/k);
            sum -= nums[i-k+1];
        }
        return ans;
    }
}

2090. 半径为 k 的子数组平均值

给你一个下标从 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 的子数组平均值 。

x 个元素的 平均值 是 x 个元素相加之和除以 x ,此时使用截断式 整数除法 ,即需要去掉结果的小数部分。

例如,四个元素 2、3、1 和 5 的平均值是 (2 + 3 + 1 + 5) / 4 = 11 / 4 = 2.75,截断后得到 2 。

示例 1:

输入:nums = [7,4,3,9,1,8,5,2,6], k = 3
输出:[-1,-1,-1,5,4,4,-1,-1,-1]
解释:

  • avg[0]、avg[1] 和 avg[2] 是 -1 ,因为在这几个下标前的元素数量都不足 k 个。
  • 中心为下标 3 且半径为 3 的子数组的元素总和是:7 + 4 + 3 + 9 + 1 + 8 + 5 = 37 。
    使用截断式 整数除法,avg[3] = 37 / 7 = 5 。
  • 中心为下标 4 的子数组,avg[4] = (4 + 3 + 9 + 1 + 8 + 5 + 2) / 7 = 4 。
  • 中心为下标 5 的子数组,avg[5] = (3 + 9 + 1 + 8 + 5 + 2 + 6) / 7 = 4 。
  • avg[6]、avg[7] 和 avg[8] 是 -1 ,因为在这几个下标后的元素数量都不足 k 个。
    示例 2:

输入:nums = [100000], k = 0
输出:[100000]
解释:

  • 中心为下标 0 且半径 0 的子数组的元素总和是:100000 。
    avg[0] = 100000 / 1 = 100000 。
    示例 3:

输入:nums = [8], k = 100000
输出:[-1]
解释:

  • avg[0] 是 -1 ,因为在下标 0 前后的元素数量均不足 k 。

提示:

n == nums.length
1 <= n <= 1e5
0 <= nums[i], k <= 1e5

注:此题中窗口的总和可能超过int的最大值!要用long

class Solution {
    public int[] getAverages(int[] nums, int k) {
        int len = nums.length;
        //滑动窗口长度
        int size = 2 * k + 1;
        int[] arr = new int[len];
        Arrays.fill(arr,-1);//初始化全为-1
        long sum = 0; //窗口元素总和(注意类型!)
        for(int i=0;i<len;i++){
            sum += nums[i];
            //不足k个元素(左边)
            if(i-k<0)continue;
            //不足k个元素(右边),直接通过第7行的fill!
            //size为滑动窗口的长度
            if(i-size+1<0)continue;
            //窗口中心记录答案
            arr[i-k] = (int)(sum/size);
            sum -= nums[i-k*2];
        }
        return arr;
    }
}
  • 来源:力扣
  • 作者:灵茶山艾府

Author: havenochoice
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source havenochoice !
评论
  TOC