题目重点:好子数组的每步差都为+1或都为−1,求各子数组元素和再相加。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 采用示例约束:单元素合法;长度≥2的全部相邻差必须是同一个+1或−1。
- 求sum(sum(subarray)),不是合法区间个数。若正式合同仅要求绝对差1,方向切换也合法,应改状态。
解题路线
- 维护ending_sum,表示全部以当前i结束的好子数组的元素和之和;总答案累加各ending_sum。
- 若差与前一合法方向相同,有run个连续合法边,能从上一位置延伸所有合法后缀,加当前x到run个旧后缀,再加单元素x,因此ending_sum增x×(run+1)。
- 方向刚切换时只剩单元素[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?
