2762-不间断子数组

Raphael Liu Lv10

给你一个下标从 0 开始的整数数组 numsnums 的一个子数组如果满足以下条件,那么它是 不间断 的:

  • ii + 1 ,…,j 表示子数组中的下标。对于所有满足 i <= i1, i2 <= j 的下标对,都有 0 <= |nums[i1] - nums[i2]| <= 2

请你返回 不间断 子数组的总数目。

子数组是一个数组中一段连续 非空 的元素序列。

示例 1:

**输入:** nums = [5,4,2,4]
**输出:** 8
**解释:**
大小为 1 的不间断子数组:[5], [4], [2], [4] 。
大小为 2 的不间断子数组:[5,4], [4,2], [2,4] 。
大小为 3 的不间断子数组:[4,2,4] 。
没有大小为 4 的不间断子数组。
不间断子数组的总数目为 4 + 3 + 1 = 8 。
除了这些以外,没有别的不间断子数组。

示例 2:

**输入:** nums = [1,2,3]
**输出:** 6
**解释:**
大小为 1 的不间断子数组:[1], [2], [3] 。
大小为 2 的不间断子数组:[1,2], [2,3] 。
大小为 3 的不间断子数组:[1,2,3] 。
不间断子数组的总数目为 3 + 2 + 1 = 6 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109

代码框架是 滑动窗口(双指针) 。在遍历数组的同时,维护窗口内的数字。

由于绝对差至多为 2,所以用平衡树或者哈希表维护都行,反正至多维护 3 个数,添加删除可以视作是 \mathcal{O}(1) 的。(如果用哈希表,还需记录数字的出现次数。)

如果窗口内的最大值与最小值的差大于 2,就不断移动左端点 left,减少窗口内的数字。

最后

[\textit{left},\textit{right}],[\textit{left}+1,\textit{right}],\cdots,[\textit{right},\textit{right}]

这一共 right}-\textit{left}+1 个子数组都是合法的,加入答案。

[sol-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def continuousSubarrays(self, nums: List[int]) -> int:
ans = left = 0
cnt = Counter()
for right, x in enumerate(nums):
cnt[x] += 1
while max(cnt) - min(cnt) > 2:
y = nums[left]
cnt[y] -= 1
if cnt[y] == 0: del cnt[y]
left += 1
ans += right - left + 1
return ans
[sol-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public long continuousSubarrays(int[] nums) {
long ans = 0;
var t = new TreeMap<Integer, Integer>();
int left = 0;
for (int right = 0; right < nums.length; right++) {
t.merge(nums[right], 1, Integer::sum);
while (t.lastKey() - t.firstKey() > 2) {
int y = nums[left++];
if (t.get(y) == 1) t.remove(y);
else t.merge(y, -1, Integer::sum);
}
ans += right - left + 1;
}
return ans;
}
}
[sol-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
long long continuousSubarrays(vector<int> &nums) {
long long ans = 0;
multiset<int> s;
int left = 0, n = nums.size();
for (int right = 0; right < n; right++) {
s.insert(nums[right]);
while (*s.rbegin() - *s.begin() > 2)
s.erase(s.find(nums[left++]));
ans += right - left + 1;
}
return ans;
}
};
[sol-Go]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func continuousSubarrays(a []int) (ans int64) {
cnt := map[int]int{}
left := 0
for right, x := range a {
cnt[x]++
for {
mx, mn := x, x
for k := range cnt {
mx = max(mx, k)
mn = min(mn, k)
}
if mx-mn <= 2 {
break
}
y := a[left]
if cnt[y]--; cnt[y] == 0 {
delete(cnt, y)
}
left++
}
ans += int64(right - left + 1)
}
return
}

func max(a, b int) int { if b > a { return b }; return a }
func min(a, b int) int { if b < a { return b }; return a }

复杂度分析

  • 时间复杂度:\mathcal{O}(n),其中 n 为 nums 的长度。
  • 空间复杂度:\mathcal{O}(1)。注意至多维护 3 个数,仅用到常量额外空间。

相似题目

 Comments
On this page
2762-不间断子数组