前からの 合計と 辞書で 和が ちょうど k に なる 区間を数える
日ごとの
海外で定番の問題リスト 4 本には入っていません
問題
小さなnums と、k をk に
- 区間は、
並びの 中で 途切れずに 続いた 部分です。 飛ばして 拾った 組は 数えません - 始まりか
終わりの 位置が 違えば、 中身が 同じでも 別の 区間として数えます numsの長さは 1 以上 20000 以下です。 値は -1000 以上 1000 以下で、 負の 数も 0 も 入ります。 kは -10000000 以上 10000000 以下です
入力と出力の例
例 1
合計が 3 に[3]、[-1, 3, 1]、[1, -2, 4]、[2, -1, 3, 1, -2] の 4 つです。
例 2
隣り合った 2 日の
例 3
増えて
面接での聞かれ方
面接では、
- 「和が k に
なる 連続した 部分配列の 数を 数えてください」 - 「負の数は入りますか?」と、
自分から 確かめるかを 見られます - 書き終えた
あとに 「全部が 正の 数なら、 もっと 記憶を 減らせますか?」と 広げられる こともあります
負の
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「ハッシュマップ」に、
関わる学び
ハッシュは、
まず 考える手順
段 1
素直にk に
段 2
前からのs[i] とs[j] - s[i] です。k にs[i] = s[j] - k の
段 3
そこで、s にs - k がk にs を
段 4
最初に、[2, -1, 3, 1, -2] は、
解答
Python
Python
from collections import Counter
def count_sum_runs(nums, k):
seen = Counter({0: 1})
s = 0
count = 0
for x in nums:
s += x
count += seen[s - k]
seen[s] += 1
return countJavaScript
JavaScript
function countSumRuns(
nums,
k
) {
const seen = new Map();
seen.set(0, 1);
let s = 0;
let count = 0;
for (const x of nums) {
s += x;
const hit = seen.get(s - k);
count += hit ?? 0;
const n = seen.get(s) ?? 0;
seen.set(s, n + 1);
}
return count;
}Go
Go
func countSumRuns(
nums []int,
k int,
) int {
seen := map[int]int{0: 1}
s := 0
count := 0
for _, x := range nums {
s += x
count += seen[s-k]
seen[s]++
}
return count
}s は、s - k のs をk が 0 のCounter と Go の map は、
計算量
- 時間は O(n) です。
並びを 前から 1 回なめ、 辞書を 引くのも 足すのも 平均で 一定の手間です - 使う
記憶は O(n) です。 前からの 合計は、 多くて n + 1 通りの 値が 辞書に入ります - 段 1 の
全部試す 書き方は、 記憶は O(1) ですが、 時間が O(n²) です
よく ある間違い
- 窓を
伸び縮みさせる 書き方を使う。 合計が kを越えたら 左を 縮める 書き方は、 全部が 正の 数の ときだけ正しく 動きます。 例 3 のように 負の 数が あると、 縮めても 合計が 減るとは 限らず、 区間を 数え落とします - 辞書の
最初に 合計 0 を 入れ忘れる。 1 日目から 始まる 区間が 数えられません。 例 2 なら、 最初の [1, 1]を落として 2 を返します - 辞書に
入れてから 回数を足す。 kが 0 のとき、 いまの 合計どうしを 引いた 「長さ 0 の区間」まで 数えます。 例 3 なら 4 ではなく 8 を返します - 回数ではなく、
出たか どうかだけを覚える。 同じ 前からの 合計が 何回も 出る 並び (例 3 や 0 が 続く 並び) で、 区間を 少なく 数えます。 辞書には 回数を 持たせます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「負の
数が 入るので、 窓を 縮める 書き方は 使えません。 前からの 合計を 使います。 区間の 合計は 前からの 合計の 差なので、 いまの 合計 s に 対して s - k が 前に 何回出たかを 数えれば、 いまの 位置で 終わる 答えの 区間の 数が 分かります。 前からの 合計の 出た 回数を 辞書に 持ち、 最初に 0 を 1 回入れておきます。 時間も 記憶も O(n) です。」
書き終えたら、
「例 2 で
たどります。 辞書は 0 が 1 回から 始めます。 1 つ目で 合計 1、 1 - 2 の -1 は 無いので 0 回、 1 を 入れます。 2 つ目で 合計 2、 0 が 1 回あるので 答えは 1。 3 つ目で 合計 3、 1 が 1 回で 答えは 2。 4 つ目で 合計 4、 2 が 1 回で 答えは 3 です。」