SWE / OA · Coding Assessment

Microsoft OA 非递减数组从指定位置累加:严格小于目标的最长前缀

非递减数组仍可能有负数;前缀和先降后升,不能直接全区间二分,需从最小值位置查找最右可行长度。

题目重点:非递减数组仍可能有负数;前缀和先降后升,不能直接全区间二分,需从最小值位置查找最右可行长度。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 数组全局非递减,元素可为负、零或正;选取的是从 start 开始连续的前 k 个,而非跳选。
  • 比较严格为 sum<target;只返回最大 k,若没有任何非空可行前缀返回 0。
  • start 可等于 n,返回 0;不合法 start 或违反排序前提报错。
  • 训练版允许 k=0 作为默认无解结果,但不把空前缀称为满足严格不等式。

解题路线

  1. 从 start 起建局部前缀和 P[k]。因为待加数组非递减,P 的相邻增量先是负数、再可能是零、最后是正数,所以 P 先不增后不减,整体未必单调。
  2. 用 bisect_left 找第一项非负值的位置,消费所有负数后得到最小前缀和的位置 k0。若 P[k0]≥target,任何非空前缀都不满足。
  3. 在 k≥k0 的右半段,P 非递减,P[k]<target 构成真前缀;二分最右满足的 k。这个 k 也是全域最长可行长度,因为左半段索引更小。
  4. 如果只问一次,直接线性扫描全部前缀并更新最大合法 k 更简单也稳;二分在同一数组做大量查询且有前缀和预处理时更有价值。

手动走一遍

[-5,-1,2,4] 从 0 起的非空前缀和依次 -5,-6,-4,0;目标 -3,前三个都严格小于 -3,第四个不满足,答案 3。若草率地假设前缀和一直递增,会在 -5→-6 处违反二分条件。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from bisect import bisect_left
from collections.abc import Sequence


def maximum_count_below_target(values: Sequence[int], start: int, target: int) -> int:
    """Maximum count from start with sum strictly below target; 0 if none."""
    n = len(values)
    if not 0 <= start <= n or any(values[i] > values[i + 1] for i in range(n - 1)):
        raise ValueError("invalid start or nondecreasing invariant")
    if start == n:
        return 0
    prefix = [0]
    for value in values[start:]:
        prefix.append(prefix[-1] + value)
    minimum_at = bisect_left(values, 0, lo=start) - start
    if prefix[minimum_at] >= target:
        return 0
    lo, hi = minimum_at, len(prefix)
    while lo + 1 < hi:
        mid = (lo + hi) // 2
        if prefix[mid] < target:
            lo = mid
        else:
            hi = mid
    return lo


if __name__ == "__main__":
    assert maximum_count_below_target([-5, -1, 2, 4], 0, -3) == 3

复杂度

本代码为单次查询建局部前缀 O(n−start) 时间/空间,并用 O(log n) 找转折与右界;总仍 O(n) 时间。若预先存全局前缀和以支持多查询,每次可用 O(log n) 找转折及右边界,空间 O(n),但应处理不同 start 的平移和负值。

易错点与追问

容易失分的边界

  • 把非递减元素误当非负元素,直接对全部前缀和二分。
  • 把严格 < 写成 ≤,恰好等于 target 时误算。
  • 只从数组开头累加而忽略给定 index。
  • 未确认可否返回 0 就称其为原题唯一接口。

继续深挖

  • 若输入保证所有元素非负,能否直接在全部局部前缀和上二分?
  • 有很多 (start,target) 查询时如何一次预处理全局前缀和?
  • 若允许跳选元素而非连续取前缀,问题如何变化?
✓已复制微信号!