技术岗位 · Coding

所有±1等差好子数组的元素和:线性累计结束贡献

好子数组的每步差都为+1或都为−1,求各子数组元素和再相加。

题目重点:好子数组的每步差都为+1或都为−1,求各子数组元素和再相加。

题意与假设

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

  • 采用示例约束:单元素合法;长度≥2的全部相邻差必须是同一个+1或−1。
  • 求sum(sum(subarray)),不是合法区间个数。若正式合同仅要求绝对差1,方向切换也合法,应改状态。

解题路线

  1. 维护ending_sum,表示全部以当前i结束的好子数组的元素和之和;总答案累加各ending_sum。
  2. 若差与前一合法方向相同,有run个连续合法边,能从上一位置延伸所有合法后缀,加当前x到run个旧后缀,再加单元素x,因此ending_sum增x×(run+1)。
  3. 方向刚切换时只剩单元素[x]与两元素[prev,x],贡献prev+2x;非±1则只剩x。每个区间归属于唯一右端,避免重复或漏算。

手动走一遍

[3,5,6,7,6]:单点贡献27;[5,6]11、[6,7]13、[5,6,7]18、[7,6]13,合计82。[1,2,1]合法单点和4、两对3+3,合计10;全段方向切换不合法。

Python 参考实现

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

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

def sum_good(a):
    if not a:
        return 0
    total = ending_sum = a[0]
    same_run, prev_diff = 0, None
    for i in range(1, len(a)):
        x, diff = a[i], a[i] - a[i - 1]
        if diff not in (-1, 1):
            same_run, prev_diff, ending_sum = 0, None, x
        elif diff == prev_diff:
            same_run += 1
            ending_sum += x * (same_run + 1)
        else:
            same_run, prev_diff = 1, diff
            ending_sum = a[i - 1] + 2 * x
        total += ending_sum
    return total

复杂度

O(n)时间、O(1)空间(任意精度整数位成本另计)。若按题面模M返回,可每步取模,但方向比较仍用原整数。

易错点与追问

容易失分的边界

  • 只加run+1得到的是区间数,遗漏元素值。
  • 看到difference ±1不能忽略示例排除了[5,6,7,6];需问清是单调单位步长还是任意折返。

继续深挖

  • 若允许任意折返±1,ending_sum转移如何简化?
  • 如何推广到固定等差d,而非只允许d=±1?
✓已复制微信号!