1343. 大小为 K 且平均值大于等于阈值的子数组数目

中等

相关标签

相关企业

提示

给你一个整数数组 arr 和两个整数 k 和 threshold 。

请你返回长度为 k 且平均值大于等于 threshold 的子数组数目。

示例 1:

输入:arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4
输出:3
解释:子数组 [2,5,5],[5,5,5] 和 [5,5,8] 的平均值分别为 4,5 和 6 。其他长度为 3 的子数组的平均值都小于 4 (threshold 的值)。

示例 2:

输入:arr = [11,13,17,23,29,31,7,5,2,3], k = 3, threshold = 5
输出:6
解释:前 6 个长度为 3 的子数组平均值都大于 5 。注意平均值不是整数。

提示:

  • 1 <= arr.length <= 105
  • 1 <= arr[i] <= 104
  • 1 <= k <= arr.length
  • 0 <= threshold <= 104

代码:

class Solution {
public:
    int numOfSubarrays(vector<int>& arr, int k, int threshold) {
        int cur = 0;
        int count = 0;
        for (int i = 0; i < k; i++) {
            cur += arr[i];
        }
        if (cur / k >= threshold) {
            count++;
        }
        for (int i = k; i < arr.size(); i++) {
            cur += (arr[i] - arr[i - k]);
            if (cur / k >= threshold) {
                count++;
            }
        }
        return count;
    }
};

<think>嗯,用户的问题是关于如何用C语言实现一个满足特定条件的区间计数问题,按照labuladong的滑动窗口框架来写。我得仔细看看题目要求。 首先,题目要求找出所有长度在[minLen, maxLen]之间的子数组,使得它们的平均值大于等于threshold。那如何高效计算这些子数组的数量呢? 直接暴力解法的话,对于每个可能的子数组,计算它的平均值,这样时间复杂度会是O(n * maxLen),因为每个子数组的长度可能有maxLen种情况,每次计算平均需要O(k)时间,总共有O(n^2)的可能,这在n=1e4的情况下可能会超时。那有没有更高效的方法? 这时候想到滑动窗口或者前缀和。比如,先计算前缀和数组,这样每个子数组的和可以用前缀和的差来表示。这样,计算每个子数组的和只需要O(1)时间。然后,对于每个右端点j,需要找到满足条件的i的位置,使得sum[j] - sum[i] >= threshold * (j - i),并j - i在minLen到maxLen之间。 不过,这可能还不够。因为对于每个j,要检查的i的范围可能很大。这时候可能需要更高效的方法,比如对于每个可能的窗口长度,应用滑动窗口技巧。例如,针对每个可能的长度L(从minLen到maxLen),滑动窗口计算每个位置的总和,然后判断平均是否满足条件。这样时间复杂度是O((maxLen - minLen +1)*n),当maxLen是1e3,n是1e4的话,这样的复杂度会是1e7,这应该在时间范围内是可行的。 那这样思路就明确了。首先,遍历所有可能的窗口长度L,从minLen到maxLen。对于每个L,计算每个窗口的总和,判断平均是否大于等于threshold。然后累加所有符合条件的窗口数目。 那具体怎么实现呢?比如,对于窗口长度L,遍历数组中的每个窗口,计算总和,然后判断sum >= threshold * L。这样每个窗口的总和可以用滑动窗口的方法优化,比如前一个窗口的和减去离开的元素,加上新进入的元素。这样每个窗口的计算是O(1),总共有n-L+1个窗口,所以每个L的处理时间是O(n)。 总的时间复杂度是O((maxLen - minLen +1)*n)。当maxLen是1e3,n是1e4的话,总共有1e3 * 1e4 = 1e7次操作,这在C语言中应该是可以接受的。 那这样的思路应该可行。接下来需要将这个思路转化为C代码。需要注意输入参数的处理,比如records数组的长度,以及minLen和maxLen的取值范围是否正确。 另外,关于数据类型,因为records中的元素可能到1e4,每个元素最大是1e4,窗口长度最多是1e3,所以总和可能到1e4 * 1e3 = 1e7,用int类型应该没问题,不会溢出。但如果threshold是1e4的话,threshold * L可能到1e4 * 1e3 = 1e7,所以sum和比较的值都是int,没问题。 那具体实现步骤: 1. 遍历所有可能的窗口长度L,从minLen到maxLen。 2. 对于每个L: a. 计算窗口的初始和sum。 b. 滑动窗口,每次移动一位,更新sum,并判断是否满足sum >= threshold * L。 c. 统计每个L下符合条件的窗口数目。 3. 所有L的结果相加,返回总数。 需要注意窗口的初始值的计算。比如,对于窗口长度L,初始窗口是前L个元素的和。然后从i=L开始,每次减去前一个元素,加上当前元素。 比如,C代码中可以用循环来处理每个L: for (L = minLen; L <= maxLen; L++) { int window_sum = 0; // 初始化窗口的前L项和 for (int i = 0; i < L; i++) { window_sum += records[i]; } if (window_sum >= threshold * L) { count++; } // 滑动窗口 for (int i = L; i < recordsSize; i++) { window_sum += records[i] - records[i - L]; if (window_sum >= threshold * L) { count++; } } } 这样每个窗口长度L的处理时间是O(n),总的时间复杂度是O((maxLen - minLen +1)*n)。对于n=1e4,maxLen=1e3,这会是1e7次运算,应该没问题。 那这样代码的大体结构就出来了。需要注意边界条件,比如当minLen等于maxLen的时候,或者当recordsSize小于minLen的情况?但题目中的输入条件已经保证了maxLen <= records.length,所以minLen <= maxLen <= recordsSize,所以每个L对应的窗口长度是有效的。 那函数的实现应该按照上述逻辑。现在,编写C代码的时候,需要注意变量的定义,比如size_t recordsSize可能为unsigned,但循环中的i可能需要用int或者size_t。例如,在初始循环中,当i从0到L-1,可能需要强制转换或者确保类型匹配。 另外,threshold可能是0的情况,这时候所有元素的和如果非负的话都会满足条件。所以代码不需要特殊处理。 测试样例的话,比如: 比如records = [2,2,2,2,5,5,5,8], threshold=4,minLen=3, maxLen=3。这时候要找长度为3的子数组的平均是否>=4。例如,子数组[2,2,5]的平均是3,不满足。而[5,5,5]的平均是5满足。这样每个符合条件的窗口都要被统计。 另一个例子,当records中所有元素都是threshold,那么每个窗口的平均等于threshold,所以所有符合条件的窗口数目是各个长度下的可能数目。 那这样的算法应该正确。 现在,将这些思路转化为C代码。函数的框架如下: static int CountIntervals(const int *records, size_t recordsSize, LengthRange lengthRange, int threshold) { int count = 0; int minLen = lengthRange.min; int maxLen = lengthRange.max; for (int L = minLen; L <= maxLen; L++) { // 处理每个窗口长度L int sum = 0; // 初始化sum为前L个元素的和 for (int i = 0; i < L; i++) { sum += records[i]; } // 检查初始窗口 if (sum >= (long long)threshold * L) { // 使用long long防止溢出? count++; } // 滑动窗口 for (int i = L; i < recordsSize; i++) { sum += records[i] - records[i - L]; if (sum >= (long long)threshold * L) { count++; } } } return count; } 这里可能需要注意的是,当threshold * L可能会溢出int。例如,当threshold是1e4,L是1e3,那么1e4 * 1e3 = 1e7,这在int的范围内(假设int是32位的话,最大值是2e9左右)。所以没问题。sum的值也是,每个元素最多1e4,L最多1e3,sum最多是1e7。所以int足够。但比较的时候,将threshold * L转换为long long或者long类型可能更好,避免中间结果溢出。比如,在C中,如果threshold和L都是int,那么它们的乘积可能溢出int。比如,假设int是32位的,最大值为2,147,483,647。当threshold和L都是1e4的话,乘积是1e8,超过int的最大值,导致溢出。这时候比较sum >= threshold * L会错误。 哦,这个问题非常关键!比如,假设threshold是1e4,L是1e4,但maxLen可能到1e3,所以threshold*L最大是1e4 * 1e3 = 1e7,这在int范围内(因为int的最大值约为2e9)。所以没问题?是的,1e7比2e9小很多,所以不会溢出。那这时候threshold * L可以保持为int类型。但是,假设sum是int类型,那么sum >= threshold * L的比较是正确的。 或者,有没有可能sum是负数?比如,当records[i]的值为负数的时候?但题目中给出的输入条件中records[i]的范围是0到1e4,所以sum不可能为负数。但题目中允许records[i]为0,所以当threshold为0的时候,所有窗口都满足条件? 所以,sum >= threshold * L的判断是正确的。但必须确保threshold * L的乘积不会溢出int。比如,当threshold和L都很大时,比如threshold是1e4,L是1e3,乘积是1e7,这在int范围内没问题。 那在C代码中,sum >= threshold * L的判断是否正确? 是的,没问题。所以不需要强制转换成更长的类型。 但是,假设在某些情况下,threshold和L的乘积可能超过int的最大值,比如当threshold是1e5,L是2e5,那乘积是2e10,这时候就需要用long long。但在本题的输入约束中,threshold<=1e4,maxLen<=1e3,所以threshold * maxLen <= 1e7,这在int范围内。所以不需要。 综上,代码中的判断sum >= threshold * L是正确的。 那现在,代码应该可以正确运行。那现在把这个思路写成C代码,按照用户提供的函数签名。 另外,LengthRange结构体的定义可能需要用户自己定义,但用户给出的函数签名中参数是LengthRange lengthRange。假设该结构体有两个int成员,比如min和max。例如: typedef struct { int min; int max; } LengthRange; 这样在函数中可以通过lengthRange.min和lengthRange.max访问。 综上,整个函数的代码应该如上述思路。 需要注意的是,recordsSize的类型是size_t,这可能是一个无符号类型。例如,在64位系统中,size_t是unsigned long。所以在循环中,比如当L的范围可能到maxLen <= recordsSize的情况下,当recordsSize是size_t类型,而循环变量是int的话,需要确保比较正确。比如,当recordsSize是很大的时候,比如超过INT_MAX的情况,但题目中records.length <= 1e4,所以int是足够的。 在循环变量的定义中,比如i < L的循环,i的类型是int,而recordsSize是size_t,但1e4以内的值不会有问题。 综上,最终的C代码应该如下。同时,需要处理可能的边界情况,比如当minLen等于maxLen的情况,或者当窗口长度等于数组长度的情况。 现在,回答用户的问题,并给出相应的代码。同时,用户的问题还要求按照labuladong的滑动窗口框架来写。labuladong的滑动窗口框架通常包括初始化左右指针,循环右指针,调整窗口内容,维护窗口状态等。例如,在滑动窗口中,可能维护一个窗口的和,然后不断移动右指针,调整左指针。但在这个问题中,因为窗口的长度是固定的,所以每个窗口的长度L下的处理是固定大小的滑动窗口,这可能更类似于固定窗口大小的滑动问题,如求每个固定窗口的和。 因此,上述的代码思路符合固定窗口大小的滑动窗口处理方式,符合labuladong的框架思想。 综上,写出代码,并测试可能的边界情况。</think>要解决这个问题,我们需要找到所有长度在 `[minLen, maxLen]` 范围内的子数组这些子数组平均值大于等于给定的 `threshold`。我们可以利用滑动窗口技巧来高效地处理每个可能的窗口长度。 --- ### 解题思路 1. **滑动窗口技巧**:对于每个可能的窗口长度 `L`(从 `minLen` 到 `maxLen`),我们使用滑动窗口来计算每个窗口的总和。 2. **窗口总和计算**:对于每个窗口长度 `L`,我们首先计算前 `L` 个元素的总和,然后通过滑动窗口的方式逐步更新总和(每次移动一位,减去离开窗口的元素,加上进入窗口的元素)。 3. **条件判断**:每次计算窗口总和后,判断该总和是否大于等于 `threshold * L`。如果是,则计数器加一。 这种方法的时间复杂度为 $O((maxLen - minLen + 1) \times n)$,其中 `n` 是数组长度。在题目给定的约束条件下,这种解法是高效的。 --- ### 代码实现 ```c typedef struct { int min; int max; } LengthRange; static int CountIntervals(const int *records, size_t recordsSize, LengthRange lengthRange, int threshold) { int count = 0; int minLen = lengthRange.min; int maxLen = lengthRange.max; for (int L = minLen; L <= maxLen; L++) { int windowSum = 0; // 初始化窗口的总和 for (int i = 0; i < L; i++) { windowSum += records[i]; } // 检查初始窗口是否满足条件 if (windowSum >= threshold * L) { count++; } // 滑动窗口 for (size_t i = L; i < recordsSize; i++) { windowSum += records[i] - records[i - L]; if (windowSum >= threshold * L) { count++; } } } return count; } ``` --- ### 示例说明 假设输入如下: - `records = [2, 2, 2, 2, 5, 5, 5, 8]` - `lengthRange = {min: 3, max: 3}` - `threshold = 4` 对于长度为 3 的窗口,我们计算每个窗口的总和: - `[2, 2, 2]` → 6 → 平均 2 - `[2, 2, 5]` → 9 → 平均 3 - `[2, 5, 5]` → 12 → 平均 4 ✅ - `[5, 5, 5]` → 15 → 平均 5 ✅ - `[5, 5, 8]` → 18 → 平均 6 ✅ 最终符合条件的窗口有 3 个。 --- ### 时间复杂度分析 - 外层循环遍历窗口长度 `L`,次数为 `maxLen - minLen + 1`。 - 内层循环遍历数组,每次处理一个窗口,复杂度为 $O(n)$。 - 总体时间复杂度为 $O((maxLen - minLen + 1) \times n)$。 --- ### 相关问题 1. 如何优化窗口长度遍历的顺序以提高缓存命中率? 2. 如果 `threshold` 为浮点数,如何修改代码以避免精度问题? 3. 如何扩展该算法以处理二维数组的类似问题? 4. 在窗口长度固定的情况下,如何进一步优化滑动窗口的初始化过程? 5. 如何处理窗口长度范围较大(如 `maxLen - minLen` 接近 1e5)的情况?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值