前からの合計と辞書で和がちょうど k になる区間を数える

日ごとの増減の並びから、続いた日の合計がちょうど k になる区間の数を数えます。前からの合計を辞書に数えておき、「いまの合計から k を引いた値」が何回出たかを足していく、ハッシュの練習です。

海外で定番の問題リスト 4 本には入っていません

問題

小さなお店の、日ごとの売上の増減を表す整数の並び nums と、整数 k を受け取ります。続いた 1 日以上の区間で、増減の合計がちょうど 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 つです。

nums = [2, -1, 3, 1, -2, 4] k = 3
4

例 2

隣り合った 2 日の組は 3 つあり、どれも合計が 2 です。中身は同じでも、位置が違うので 3 つと数えます。

nums = [1, 1, 1, 1] k = 2
3

例 3

増えて減ってをくり返す並びです。合計が 0 になる区間は、隣り合った 2 日が 3 つと、4 日まるごとの 1 つで、4 つです。

nums = [5, -5, 5, -5] k = 0
4

面接での聞かれ方

面接では、並びと目標の値だけを渡されることがよくあります。

  • 「和が k になる連続した部分配列の数を数えてください」
  • 「負の数は入りますか?」と、自分から確かめるかを見られます
  • 書き終えたあとに「全部が正の数なら、もっと記憶を減らせますか?」と広げられることもあります

負の数が入るかどうかで、使える型が変わります。全部が正なら窓を伸び縮みさせる書き方が使えますが、負の数があると窓を縮めても合計が減るとは限らないので、使えません。書く前に必ず確かめます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「ハッシュマップ」に、この型の要点を表とコードでまとめています。

ハッシュは、値から番地を計算して、探す手間をほぼ 1 回にする仕組みです。重複や回数の問題は、この形で解けます。

main.py
def count_sum_runs(nums: list[int], k: int) -> int:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(count_sum_runs([2, -1, 3, 1, -2, 4], 3))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します