【每日一题】LeetCode 2841.几乎唯一子数组的最大和(数组、哈希、滑动窗口)
题目描述
给定一个整数数组 nums
和两个正整数 m
和 k
,任务是找出 nums
中长度为 k
的 几乎唯一 子数组的最大和。如果不存在这样的子数组,则返回 0
。
几乎唯一子数组 定义为:子数组中至少有 m
个互不相同的元素。
子数组是指数组中一段连续非空的元素序列。
nums
:一个整数数组。m
:一个正整数,表示至少有多少个互不相同的元素。k
:一个正整数,表示子数组的长度。
示例
示例 1
- 输入:
nums = [2,6,7,3,1,7], m = 3, k = 4
- 输出:
18
- 解释:有三个长度为
4
的几乎唯一子数组[2, 6, 7, 3]
,[6, 7, 3, 1]
和[7, 3, 1, 7]
。其中和最大的是[2, 6, 7, 3]
,和为18
。
示例 2
- 输入:
nums = [5,9,9,2,4,5,4], m = 1, k = 3
- 输出:
23
- 解释:有五个长度为
3
的几乎唯一子数组[5, 9, 9]
,[9, 9, 2]
,[9, 2, 4]
,[2, 4, 5]
和[4, 5, 4]
。其中和最大的是[5, 9, 9]
,和为23
。
示例 3
- 输入:
nums = [1,2,1,2,1,2,1], m = 3, k = 3
- 输出:
0
- 解释:不存在长度为
3
的子数组含有至少3
个互不相同元素的子数组,因此不存在几乎唯一子数组,最大和为0
。
思路分析
要解决这个问题,我们可以使用滑动窗口的方法。首先,我们初始化一个窗口,使其包含数组的前 k
个元素。然后,我们检查这个窗口是否满足至少有 m
个互不相同的元素的条件。如果满足,我们就记录下这个窗口的和。接着,我们开始滑动窗口,每次向右移动一个元素,移除窗口最左边的元素,并添加一个新的元素到窗口的右边。在每次移动后,我们都需要更新窗口内元素的计数,并检查是否仍然满足条件。如果满足,我们就更新最大和。这个过程一直持续到窗口滑动到数组的末尾。
代码实现
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Solution {
public long maxSum(List<Integer> nums, int m, int k) {
// 使用 HashMap 来存储窗口内元素的频率
Map<Integer, Integer> hashMap = new HashMap<>();
// 初始化最大和为 0
long max = 0;
// 初始化当前窗口的和
long count = 0;
// 填充第一个窗口
for (int i = 0; i < k; i++) {
int num = nums.get(i);
hashMap.put(num, hashMap.getOrDefault(num, 0) + 1);
count += num;
}
// 检查第一个窗口是否满足条件
if (hashMap.size() >= m) {
max = count;
}
// 开始滑动窗口
for (int i = k; i < nums.size(); i++) {
// 移除窗口最左边的元素
int oldnum = nums.get(i - k);
count -= oldnum;
hashMap.put(oldnum, hashMap.get(oldnum) - 1);
// 如果某个元素的计数变为 0,则从 HashMap 中移除
if (hashMap.get(oldnum) == 0) {
hashMap.remove(oldnum);
}
// 添加新元素到窗口
int newnum = nums.get(i);
hashMap.put(newnum, hashMap.getOrDefault(newnum, 0) + 1);
count += newnum;
// 检查当前窗口是否满足条件,并更新最大和
if (hashMap.size() >= m) {
max = Math.max(max, count);
}
}
return max;
}
}