题目重点:寻找最长连续子数组,其中至多一对相邻元素发生下降,明确相等是否算break。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练以给定break定义next<previous为准,相等不算break,故其余部分非降序;若实际要求严格上升,则break判断改为next<=previous并重新测试。
- 不能删除或修改元素,只允许片段内最多一个坏相邻关系。与“最多改变一个值使上升”是不同问题。
解题路线
- 记录最近下降边的右端previousDrop;遇新下降边时,窗口左端移到上一下降边的右端,从而排除旧坏边但保留新坏边。
- 每次处理right,窗口里至多一个下降,且left是能保证该性质的最早位置,故其长度是以right结束的最长合法长度。更新全局best。
- 不要把窗口移到新下降右端,那会同时丢掉允许保留的一次下降,退化为不允许break。空输入结果0。
手动走一遍
[1,4,2,3,1,5]:第二次下降在3→1,需把左端移到第一个下降的右端2所在位置,窗口[2,3,1,5]仍只含一次下降。最优长度4;[1,1,1]按本训练合同长度3。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def longest_one_drop(values):
"""Longest nonempty contiguous window with <=1 edge where next < previous.
Equal adjacent values do NOT count as a break under this declared contract.
"""
left=0
previous_drop=None
best=0
for right,value in enumerate(values):
if right and value<values[right-1]:
if previous_drop is not None:left=previous_drop
previous_drop=right
best=max(best,right-left+1)
return best
复杂度
O(n)时间/O(1)辅助空间;所有状态仅left、最近断点与best。
易错点与追问
容易失分的边界
- 文字“递增”与显式break<可能对等值有不同解释,必须先举[1,1]澄清。
- 用修一个元素的解法会漏掉“保留下降但计次数”的合法片段。
继续深挖
- 允许最多k次下降,用队列保存坏边右端,超过k弹最早坏边并移动left;O(n)时间/O(k)状态。
- 若要求返回片段,保存最佳(left,right)而非复制每个窗口。
