Medium
题目描述
给你一个整数数组 nums 和一个整数 k。找出所有满足以下条件的 nums 子数组的最大子数组和:
- 子数组的长度是
k,且 - 子数组中的所有元素都不相同。
返回满足条件的所有子数组的最大子数组和。如果没有子数组满足条件,返回 0。
子数组是数组中一个连续的非空元素序列。
示例 1:
输入:nums = [1,5,4,2,9,9,9], k = 3
输出:15
解释:长度为 3 的 nums 子数组为:
- [1,5,4] 满足要求,和为 10。
- [5,4,2] 满足要求,和为 11。
- [4,2,9] 满足要求,和为 15。
- [2,9,9] 不满足要求,因为元素 9 重复了。
- [9,9,9] 不满足要求,因为元素 9 重复了。
我们返回 15,因为它是满足条件的所有子数组中的最大子数组和。
示例 2:
输入:nums = [4,4,4], k = 3
输出:0
解释:长度为 3 的 nums 子数组为:
- [4,4,4] 不满足要求,因为元素 4 重复了。
我们返回 0,因为没有子数组满足条件。
约束条件:
1 <= k <= nums.length <= 10^51 <= nums[i] <= 10^5
解题思路
这是一道典型的滑动窗口问题,需要维护一个长度为 k 的窗口,并检查窗口内元素是否都不相同。
解题思路
方法一:滑动窗口 + 哈希表(推荐)
- 使用滑动窗口技术,维护一个大小为 k 的窗口
- 用哈希表记录窗口内每个元素的频次
- 用一个变量记录当前窗口的和
- 当窗口大小达到 k 时:
- 检查窗口内是否所有元素都不重复(哈希表大小等于 k)
- 如果满足条件,更新最大和
- 移除窗口左端元素,为下次迭代做准备
优化点:
- 使用频次计数而不是集合,能更高效地处理元素的添加和删除
- 维护窗口和,避免每次重新计算
时间复杂度为 O(n),空间复杂度为 O(k),其中 n 是数组长度。每个元素最多被访问两次(加入和移出窗口各一次)。
代码实现
class Solution {
public:
long long maximumSubarraySum(vector<int>& nums, int k) {
int n = nums.size();
unordered_map<int, int> freq;
long long currentSum = 0;
long long maxSum = 0;
for (int i = 0; i < n; i++) {
// 添加当前元素到窗口
freq[nums[i]]++;
currentSum += nums[i];
// 如果窗口大小超过k,移除左端元素
if (i >= k) {
int leftElement = nums[i - k];
freq[leftElement]--;
if (freq[leftElement] == 0) {
freq.erase(leftElement);
}
currentSum -= leftElement;
}
// 检查当前窗口是否满足条件
if (i >= k - 1 && freq.size() == k) {
maxSum = max(maxSum, currentSum);
}
}
return maxSum;
}
};
class Solution:
def maximumSubarraySum(self, nums: List[int], k: int) -> int:
n = len(nums)
freq = {}
current_sum = 0
max_sum = 0
for i in range(n):
# 添加当前元素到窗口
freq[nums[i]] = freq.get(nums[i], 0) + 1
current_sum += nums[i]
# 如果窗口大小超过k,移除左端元素
if i >= k:
left_element = nums[i - k]
freq[left_element] -= 1
if freq[left_element] == 0:
del freq[left_element]
current_sum -= left_element
# 检查当前窗口是否满足条件
if i >= k - 1 and len(freq) == k:
max_sum = max(max_sum, current_sum)
return max_sum
public class Solution {
public long MaximumSubarraySum(int[] nums, int k) {
int n = nums.Length;
var freq = new Dictionary<int, int>();
long currentSum = 0;
long maxSum = 0;
for (int i = 0; i < n; i++) {
// 添加当前元素到窗口
if (freq.ContainsKey(nums[i])) {
freq[nums[i]]++;
} else {
freq[nums[i]] = 1;
}
currentSum += nums[i];
// 如果窗口大小超过k,移除左端元素
if (i >= k) {
int leftElement = nums[i - k];
freq[leftElement]--;
if (freq[leftElement] == 0) {
freq.Remove(leftElement);
}
currentSum -= leftElement;
}
// 检查当前窗口是否满足条件
if (i >= k - 1 && freq.Count == k) {
maxSum = Math.Max(maxSum, currentSum);
}
}
return maxSum;
}
}
var maximumSubarraySum = function(nums, k) {
let maxSum = 0;
let currentSum = 0;
let freq = new Map();
let left = 0;
for (let right = 0; right < nums.length; right++) {
currentSum += nums[right];
freq.set(nums[right], (freq.get(nums[right]) || 0) + 1);
if (right - left + 1 > k) {
currentSum -= nums[left];
freq.set(nums[left], freq.get(nums[left]) - 1);
if (freq.get(nums[left]) === 0) {
freq.delete(nums[left]);
}
left++;
}
if (right - left + 1 === k && freq.size === k) {
maxSum = Math.max(maxSum, currentSum);
}
}
return maxSum;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n) - 每个元素最多被访问两次,哈希表操作为 O(1) |
| 空间复杂度 | O(k) - 哈希表最多存储 k 个不同元素 |
相关题目
. Max Consecutive Ones III (Medium)
. Longest Nice Subarray (Medium)
. Optimal Partition of String (Medium)
. Count the Number of Good Subarrays (Medium)
. Maximum Good Subarray Sum (Medium)