题目重点:从 Netflix Coding 长帖的错误版本和最终版本出发,解释先检查再加入、窗口左边界为何不能倒退,并返回最早的最长无重复子数组。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 输入元素可哈希;返回最长连续无重复的实际 slice,长度相同取最早出现的 slice。题目长帖返回 list,但平局规则未给。
- 空输入返回空 list;show ID 的相等语义已经确定。
- 本题要求连续子数组,不是任意去重后保留的子序列。
解题路线
- set 滑窗:右端新 show 已在 set 时,从左侧逐个删除直到重复消失,然后加入新 show;每个元素最多入/出一次。先 add 再判断会让新 show 立刻命中,正是长帖记录的 bug。
- last-index 优化:记录每个 show 上次位置;只有上次位置仍在当前窗口内,才把 left 跳到 `last+1`,用 `max(left,last+1)` 的含义保证 left 不倒退。
- 每步比较当前窗口长度;只有严格更长才保存 best_start/best_length,使并列时保留最早。最终切片一次返回,避免每次改善都复制整个窗口造成 O(n²) 输出开销。
手动走一遍
[A,B,C,A,D] 扫到第二个 A 时,上次 A 在下标 0,left 从 0 跳到 1,当前窗口 [B,C,A];再加 D 得 [B,C,A,D],返回长度 4 的 slice。若之后遇到很早已滑出窗口的元素,不应把 left 往回拉。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def longest_unique_contiguous(shows):
"""Return earliest longest contiguous slice with distinct show IDs."""
last_index = {}
left = 0
best_start = 0
best_length = 0
for right, show in enumerate(shows):
if show in last_index and last_index[show] >= left:
left = last_index[show] + 1
last_index[show] = right
length = right - left + 1
if length > best_length:
best_start, best_length = left, length
return shows[best_start:best_start + best_length]
复杂度
遍历 n 次,哈希操作预期 O(1),最后只复制一次最优 slice O(n),总预期 O(n) 时间;last-index 最多保存不同 show 数 D,空间 O(D),输出至多 O(n)。
易错点与追问
容易失分的边界
- 把右端元素先加进 visited 再检查是否已存在,必然自撞。
- 重复位置已在窗口左侧仍让 left 后退。
- 每次更新最优都切片复制,最坏退化为 O(n²)。
- 把连续无重复子数组当可跳过中间重复项的子序列。
- 用长度相等时也更新结果,悄悄改成取最晚解。
继续深挖
- 若只求长度而不返回 slice,可否减少输出拷贝?
- 如果 ID 是复杂对象不可哈希,如何定义稳定键?
- 输入无限流且要报告最近 W 项内最长无重复段时需要什么过期处理?
