题目描述
思路解析动画文字版
记牢这套「先求 k,再枚举起点、右端往右扩,集合凑够 k 就开始逐个计数」,下面每一帧都在套它。
第 0 步 · 先求不同元素总数 k:先扫全数组,不同元素是 1、3、2,一共 k = 3 种。之后每个子数组都要拿它的不同元素个数去和这个 3 比。
起点 i=0 · 加入第 0 个:起点固定在 0,右端扩到第 0 个 1。这是窗口里的新面孔,不同元素数涨到 1。
起点 i=0 · 判定:窗口不同元素才 1 种,离 k = 3 还差 2 种,这段不算完全子数组,继续把右端往右扩。
起点 i=0 · 加入第 1 个:起点固定在 0,右端扩到第 1 个 3。这是窗口里的新面孔,不同元素数涨到 2。
起点 i=0 · 判定:窗口不同元素才 2 种,离 k = 3 还差 1 种,这段不算完全子数组,继续把右端往右扩。
起点 i=0 · 加入第 2 个:起点固定在 0,右端扩到第 2 个 1。它之前已经在窗口里出现过,集合不变,不同元素数还是 2。
起点 i=0 · 判定:窗口不同元素才 2 种,离 k = 3 还差 1 种,这段不算完全子数组,继续把右端往右扩。
起点 i=0 · 加入第 3 个:起点固定在 0,右端扩到第 3 个 2。这是窗口里的新面孔,不同元素数涨到 3。
起点 i=0 · 判定:窗口不同元素 3 恰好等于 k = 3,[0, 3] 凑齐了全部 3 种,是一个完全子数组,ans 变成 1。
起点 i=0 · 加入第 4 个:起点固定在 0,右端扩到第 4 个 2。它之前已经在窗口里出现过,集合不变,不同元素数还是 3。
起点 i=0 · 判定:窗口不同元素 3 恰好等于 k = 3,[0, 4] 凑齐了全部 3 种,是一个完全子数组,ans 变成 2。
起点 i=1 · 加入第 1 个:起点固定在 1,右端扩到第 1 个 3。这是窗口里的新面孔,不同元素数涨到 1。
起点 i=1 · 判定:窗口不同元素才 1 种,离 k = 3 还差 2 种,这段不算完全子数组,继续把右端往右扩。
起点 i=1 · 加入第 2 个:起点固定在 1,右端扩到第 2 个 1。这是窗口里的新面孔,不同元素数涨到 2。
起点 i=1 · 判定:窗口不同元素才 2 种,离 k = 3 还差 1 种,这段不算完全子数组,继续把右端往右扩。
起点 i=1 · 加入第 3 个:起点固定在 1,右端扩到第 3 个 2。这是窗口里的新面孔,不同元素数涨到 3。
起点 i=1 · 判定:窗口不同元素 3 恰好等于 k = 3,[1, 3] 凑齐了全部 3 种,是一个完全子数组,ans 变成 3。
起点 i=1 · 加入第 4 个:起点固定在 1,右端扩到第 4 个 2。它之前已经在窗口里出现过,集合不变,不同元素数还是 3。
起点 i=1 · 判定:窗口不同元素 3 恰好等于 k = 3,[1, 4] 凑齐了全部 3 种,是一个完全子数组,ans 变成 4。
起点 i=2 · 第 2 个:起点 2 从第 2 个数到第 2 个,不同元素最多才 1 种,凑不齐 3 种。起点 2 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
起点 i=2 · 第 3 个:起点 2 从第 2 个数到第 3 个,不同元素最多才 2 种,凑不齐 3 种。起点 2 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
起点 i=2 · 第 4 个:起点 2 从第 2 个数到第 4 个,不同元素最多才 2 种,凑不齐 3 种。起点 2 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
起点 i=3 · 第 3 个:起点 3 从第 3 个数到第 3 个,不同元素最多才 1 种,凑不齐 3 种。起点 3 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
起点 i=3 · 第 4 个:起点 3 从第 3 个数到第 4 个,不同元素最多才 1 种,凑不齐 3 种。起点 3 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
起点 i=4 · 第 4 个:起点 4 从第 4 个数到第 4 个,不同元素最多才 1 种,凑不齐 3 种。起点 4 往后再怎么扩也只有这些元素,这一轮数不出完全子数组。
收束 · 汇总答案:五个起点都枚举完了。起点 0 数到 [0,3]、[0,4] 两个,起点 1 数到 [1,3]、[1,4] 两个,后面三个起点凑不齐 3 种。加起来完全子数组共 4 个。
边界先想清:全相同、全不同、单元素。全相同时每段都完全,答案是子数组总数。
面试重点:认出「固定右端、维护左端边界、按左端可选数累加」的滑动窗口计数母题,能把 O(n²) 压到 O(n)。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def countCompleteSubarrays(self, nums: List[int]) -> int: cnt = len(set(nums)) ans, n = 0, len(nums) for i in range(n): s = set() for x in nums[i:]: s.add(x) if len(s) == cnt: ans += 1 return ans复杂度
- 时间:O(n²),外层枚举起点、内层枚举右端,集合增删查平均 O(1)
- 空间:O(n),集合最多装下 n 个不同元素;Python 切片 nums[i:] 另有 O(n) 拷贝但不改量级
易错点
面试追问把动画讲成自己的话
追问这个 O(n²) 还能优化吗?
追问它和「至多 K 个不同整数的子数组」是一类题吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
使二进制数组全部等于 1 的最少操作次数 I
LeetCode 3191 · 中等 · 沿着 双指针套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题