题目重点:先求全数组中各不同值的最小正频次 k,再找窗口最高频次恰为 k 的最大长度;用超限计数维护 O(n) 窗口。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版输入整数数组,空数组返回 0;只对整数组中实际出现过的元素求 minFreq,不能把未出现元素频率 0 算进去。
- 候选窗口必须连续,条件是窗口中最大频次**等于**全局 k,不是窗口最小频次、不同元素数或某特定元素频次。
解题路线
- 先用 Counter 得每个不同值的全局频次,取最小 k。滑窗维护局部计数,右端加入新值后,之前所有计数≤k,只有新值可能超过 k;因此 while 该新值计数>k 时右移左端并减其计数。
- 每步窗口满足所有值出现次数≤k,记录最大长度。为何可用‘≤k’求原题的‘=k’:若全局最长合法≤k窗口的最高频次<k,且它不是整数组,就能向相邻一侧扩一格仍不超 k,矛盾;整数组则各元素频次至少 k,故也不可能全<k。
- 每个左右指针至多移动 n 次,初始计数 O(n),整体 O(n);不必在左端缩小时维护可能下降的 `mode frequency`,避免陈旧最大频次。
- 对所有长度≤8、值域 0..2 的 9,841 个数组,用 O(n²) 枚举每个子数组的 Counter 精确核对。
手动走一遍
数组 [1,1,1,2,2] 的全局频次为 3 与 2,k=2。窗口 [1,1,2,2] 中两值最高频次均 2,长度 4;整个数组的 1 出现 3 次不合法。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import Counter, defaultdict
def longest_consistent_subarray(values: list[int]) -> int:
"""Max length with window max frequency = global min positive frequency."""
if not values:
return 0
threshold = min(Counter(values).values())
counts: dict[int, int] = defaultdict(int)
left = best = 0
for right, value in enumerate(values):
counts[value] += 1
while counts[value] > threshold:
counts[values[left]] -= 1
left += 1
best = max(best, right - left + 1)
return best
复杂度
Counter 和滑窗各 O(n) 时间;两张计数表空间 O(U),U 为不同元素数。若数据是无限流,全局 minFreq 尚未确定,不能直接在线使用这套方法。
易错点与追问
容易失分的边界
- 把 k 算成当前窗口的最小频次而非整数组最小频次。
- 窗口缩小时依赖陈旧的 maxFreq,不再反映当前值计数。
- 满足≤k 的窗口一律不检查原题等号却没有论证最大窗口为何可取等号。
- 把子数组理解为非连续子序列。
- 忽略空数组无法取全局 minFreq。
继续深挖
- 如果目标改为窗口最高频次至少 k,还能用同一滑窗吗?
- 若 minFreq 随流到达会改变,能否在线维护答案?
- 若值域很大但元素少,内存怎样估?
- 为什么只需检查刚加入的值是否超限?
