30 秒识别Q1 · 724 Find Pivot Index:找出数组中的「中心下标」:它左边所有数之和等于右边所有数之和。你的第一反应是哪个模式?
看答案
前缀和。left == total - left - nums[i] 一次遍历即可。看到「左边的和 / 右边的和」就是前缀和的字面形态,连哈希都不需要。
sum(i..j) = pre[j+1] - pre[i]。一旦区间和能 O(1) 拿到,问题就退化成「找一对下标」——那是第 01 章的活。这两步是本章的全部:先把「区间」降维成「一对前缀」,再用哈希表回答「我要的那个前缀值,之前出现过几次 / 最早在哪」。「子数组和恰好为 K」于是变成:对每个右端点,问 cur - k 见过几次。
累积量不必是和。取模后能处理「能被 K 整除」,异或前缀能处理「异或为 0 的子数组」,前后缀乘积能处理 238。骨架不动,换的只是「累积什么」和「找哪个历史值」。
| 题面出现… | 想到 |
|---|---|
| 「区间和 / 范围求和」且要多次查询 | 预处理前缀和数组(模板 A) |
| 「和恰好为 K 的子数组个数 / 最长长度」 | 前缀和 + 哈希(模板 B) |
| 「除自身以外数组的乘积」且不许用除法 | 前缀积 × 后缀积 |
| 「能被 K 整除的子数组」 | 前缀和取模,同余即可整除 |
| 「0 和 1 数量相同的最长子数组」 | 把 0 记成 −1,转成「和为 0」 |
| 二维矩阵的「子矩形和」 | 二维前缀和,容斥四项 |
都带「连续子数组」四个字,分流看的是问什么:「和恰好为 K」→ 本章(元素可能为负时窗口和不单调,滑窗的收缩条件无从写起;全为正数时滑窗也做得了,但前缀和一招通吃);「和 ≥ target 的最短」且元素全为正 → 滑动窗口(第 03 章);「窗口固定大小求最值」→ 单调队列(第 04 章)。本章的哈希只是配件,主语始终是前缀和——这一点决定了你该往哪本模板里翻。
■ 【骨架】 不变,闭着眼默写 ■ 【插槽】 随题变化,写题前先问自己填什么
def build_prefix(nums):
pre = [0] * (len(nums) + 1) # 【骨架】长度 n+1,pre[0]=0 是哨兵
for i, x in enumerate(nums): # 【骨架】
pre[i + 1] = pre[i] + x # 【插槽】累积什么:和 / 乘积 / 异或 / 计数
return pre
def range_sum(pre, i, j): # 【骨架】闭区间 [i, j]
return pre[j + 1] - pre[i] # 【骨架】区间 = 两个前缀之差
i == 0 的特判。from collections import defaultdict
def subarray_sum_k(nums, k):
cnt = defaultdict(int)
cnt[0] = 1 # 【骨架】空前缀出现过一次;漏了它就漏掉"从头开始"的子数组
cur = ans = 0
for x in nums: # 【骨架】
cur += x # 【插槽】累积量:和 / 前缀和取模 / 奇偶数差
ans += cnt[cur - k] # 【插槽】要找的历史值,以及答案怎么攒:计数 / 最长 / 存在性
cnt[cur] += 1 # 【骨架】先结算再登记,否则会用自己配自己
return ans
cnt[0] = 1 必须有(代表空前缀),先结算再登记(否则会拿自己配自己,和第 01 章模板 A 是同一条纪律)。n + 1 长度的哨兵位。right 就能做到 O(1) 额外空间(输出数组不算)。被追问「有 0 怎么办」时,正是不用除法的理由。前缀和 % k,哈希里存最早下标而不是次数。两个坑:长度至少为 2,以及 first = {0: -1} 这个初始值。cnt[0] = 1——少了它,「从下标 0 开始的那些子数组」全部数不到。subarray_sum_k([3], 3) 会返回 0——模板测试里专门留了这条断言。cnt[cur] += 1 必须在 ans += cnt[cur - k] 之后。顺序反了,k == 0 时每个位置都会多算一次。pre 长度是 n + 1,闭区间 [i, j] 的和是 pre[j+1] - pre[i]。把 pre 写成长度 n(pre[i] 表示前 i+1 项)也能做,但每道题都要重新推边界,得不偿失。i - first[cur] >= 2。存最新下标会把长度 1 的情况误判成 True。30 秒识别Q1 · 724 Find Pivot Index:找出数组中的「中心下标」:它左边所有数之和等于右边所有数之和。你的第一反应是哪个模式?
前缀和。left == total - left - nums[i] 一次遍历即可。看到「左边的和 / 右边的和」就是前缀和的字面形态,连哈希都不需要。
30 秒识别Q2 · 974 Subarray Sums Divisible by K:求和能被 K 整除的连续子数组个数。你的第一反应是哪个模式?
前缀和 + 哈希(模板 B),累积量换成 前缀和 % k:两个前缀同余,它们之间那段就能被 K 整除。注意 Python 的 % 对负数已经返回非负结果,其他语言要自己修正。
30 秒识别Q3 · 求长度恰好为 k 的连续子数组的最大平均值。你的第一反应是哪个模式?
定长滑动窗口,不是本章——窗口大小固定,进一个出一个就能 O(1) 维护和,根本不需要建前缀数组。前缀和是为任意区间的多次查询准备的;只查一种固定长度时它是杀鸡用牛刀。见 §2 分流条件与第 03 章模板 B。
每章自测都会混入恰好一道非本章题,防止「读哪章选哪章」的条件反射;题面也一律取自本章未收录的题,避免凭记忆而非凭信号作答。