K 个不同整数的子数组
难度:
标签:
题目描述
代码结果
运行时间: 96 ms, 内存: 18.0 MB
/*
* 思路:
* 我们使用滑动窗口来找到包含恰好k个不同整数的子数组。
* 在每一步中,我们将窗口右端扩展并记录每个整数的出现次数。
* 如果窗口内的不同整数个数大于k,则移动左端以减少不同整数的个数。
* 最终,当窗口内的不同整数个数正好为k时,记录子数组的数量。
*/
import java.util.*;
import java.util.stream.*;
public class GoodSubarraysStream {
public long countGoodSubarrays(int[] nums, int k) {
Map<Integer, Integer> countMap = new HashMap<>();
int left = 0;
long result = 0;
for (int right = 0; right < nums.length; right++) {
countMap.put(nums[right], countMap.getOrDefault(nums[right], 0) + 1);
while (countMap.size() > k) {
countMap.put(nums[left], countMap.get(nums[left]) - 1);
if (countMap.get(nums[left]) == 0) {
countMap.remove(nums[left]);
}
left++;
}
if (countMap.size() == k) {
result += right - left + 1;
}
}
return result;
}
public static void main(String[] args) {
GoodSubarraysStream gss = new GoodSubarraysStream();
System.out.println(gss.countGoodSubarrays(new int[]{1, 2, 1, 2, 3}, 2)); // 输出7
System.out.println(gss.countGoodSubarrays(new int[]{1, 2, 1, 3, 4}, 3)); // 输出3
}
}
解释
方法:
这道题要求找出数组中恰好包含 k 个不同整数的子数组数目。题解采用了双指针技术和滑动窗口的方法,结合了两个具体的函数:计算最多有 k 个不同整数的子数组数目的函数 `atMostKDistinct`,以及通过计算 `atMostKDistinct(nums, k) - atMostKDistinct(nums, k-1)` 来得到恰好有 k 个不同整数的子数组数目。这种方法使用了频率数组来跟踪每个元素的出现次数,并动态调整窗口的大小来满足条件。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在函数`atMostKDistinct`中,为什么在`nums[right]`的频率为0时,计数器`cnt`会增加?这与算法的哪个部分有关?
▷🦆
如何理解和计算`res += right - left`这一步骤中的`right - left`?它代表了什么意义?
▷🦆
在处理边界条件时,如何确保`left`指针不会超过`right`指针,特别是在右指针开始移动时?
▷🦆
在减少`freq[nums[left]]`后,为什么要检查`freq[nums[left]]`是否变为0来决定是否减少`cnt`?
▷相关问题
无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc"
,所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b"
,所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke"
,所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke"
是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成