From 2884ce71ac26452383b60499c314a21a0c34d42f Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 21 Sep 2025 19:36:13 +0900 Subject: [PATCH 1/8] Solve 560_subarray_sum_equals_k_medium --- 560_subarray_sum_equals_k_medium/README.md | 120 ++++++++++++++++++ 560_subarray_sum_equals_k_medium/step1.py | 28 ++++ .../step1_hashmap.py | 28 ++++ 560_subarray_sum_equals_k_medium/step2.py | 20 +++ .../step2_hashmap.py | 25 ++++ 5 files changed, 221 insertions(+) create mode 100644 560_subarray_sum_equals_k_medium/README.md create mode 100644 560_subarray_sum_equals_k_medium/step1.py create mode 100644 560_subarray_sum_equals_k_medium/step1_hashmap.py create mode 100644 560_subarray_sum_equals_k_medium/step2.py create mode 100644 560_subarray_sum_equals_k_medium/step2_hashmap.py diff --git a/560_subarray_sum_equals_k_medium/README.md b/560_subarray_sum_equals_k_medium/README.md new file mode 100644 index 0000000..3a75e88 --- /dev/null +++ b/560_subarray_sum_equals_k_medium/README.md @@ -0,0 +1,120 @@ +# 問題へのリンク +[560. Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/) + +# 言語 +Python + + +# 自分の解法 +- `nums`に負の数が含まれなければ、two pointersでTC: `O(n)`/ SC: `O(1)`で解けるが、負の数が含まれるので、two pointersは使えない。 + - 累積和に対して二分探索をして`O(n log n)`という解法もある。 +- すべての数に一律に大きな値(`-min(nums)`など)を足して累積和を非負にする方法も考えたが、それでは累積和が「要素数×ずらした値」の分だけずれるので、うまくいかない。 + +## step1 +二重ループを回す方法 +```python +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + num_subarrays = 0 + # cumsums[i] = sum(nums[:i]) + # sum(nums[i:j]) = cumsums[i] - cumsums[j] + cumsums = [0] * (len(nums) + 1) + for i in range(len(nums)): + cumsums[i + 1] = cumsums[i] + nums[i] + + for i in range(len(nums)): + for j in range(i + 1, len(nums) + 1): + sum_from_i_to_j = cumsums[j] - cumsums[i] + if sum_from_i_to_j == k: + num_subarrays += 1 + + return num_subarrays +``` + +`n`を`nums`の長さとすると、 +- 時間計算量:`O(n^2)` +- 空間計算量:`O(n)` + +- この空間計算量は`O(1)`にできる + + + + + +- `cumsum`の求め方は以下のような方法もある。 +```python +cumsums = [0] +for num in nums: + cumsums.append(cumsums[-1] + num) +``` +(ref: https://github.com/tokuhirat/LeetCode/pull/16/files?short_path=d4900f9#diff-d4900f989c6f9680b8e8144658ef8f10d6025523b2c0c63bed653dcdcc4fc290) + +## step2 +空間計算量を`O(1)`にする方法 +```python +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + num_subarrays = 0 + for i in range(len(nums)): + subarray_sum = 0 # sum(nums[i:j+1]) + for j in range(i, len(nums)): + subarray_sum += nums[j] + if subarray_sum == k: + num_subarrays += 1 + return num_subarrays +``` + +- `cumsum`は`cumsums[i] = sum(nums[:i])`と定義すると、元の配列より1だけ長くなるので、添え字の管理が面倒になる点に注意する。特に、本解法のようにforループを1度だけ回す場合、`range`の範囲をどうするかがポイントになる&バグを生みやすい。 + - `cumsums[i] = sum(nums[:i+1])`と定義すると、`cumsums`の長さは`len(nums)`と同じになるが、`cumsums[i] - cumsums[i-1] = nums[i]`が`i=0`のときに成り立たないので、条件分岐が余分に必要になる。 +- cumulative sum はprefix sumとも呼ばれる。 + - cf. https://en.wikipedia.org/wiki/Prefix_sum + + + +## step3 + +## step4 (FB) + + + +# 別解・模範解答 +ハッシュマップを使う方法。時間計算量を`O(n)`にできる。 +```python +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + # k = nums[i:j] = cumsums[j] - cumsums[i] + # if cumsums[j]-k in cumsum_hashmap for j in i+1, ..., then OK + num_subarrays = 0 + # cumsums[i] = sum(nums[:i]) + cumsums = [0] * (len(nums) + 1) + for i in range(len(nums)): + cumsums[i + 1] = cumsums[i] + nums[i] + + hashmap: dict[int, int] = defaultdict(int) + for i in range(len(nums) + 1): + num_subarrays += hashmap[cumsums[i] - k] + hashmap[cumsums[i]] += 1 + return num_subarrays +``` + +- Subarray自体は必要なくて、その数だけが必要であることがミソ。数だけなら、ハッシュマップで管理すれば、`O(1)`でアクセスできる。 + - `cumsums`を使う解法ではSubarray自体が求まる +- ただし、`cumsums`の解法の空間計算量を`O(1)`にした解法とは時間計算量と空間計算量のトレードオフの関係にあるので、どちらが良いかは場合による。 + +- 時間計算量:`O(1)` +- 空間計算量:`O(n)` + +# 想定されるフォローアップ質問 + +## CS 基礎 + +## システム設計 + +## その他 + +# 次に解く問題の予告 +- [String to Integer (atoi) - LeetCode](https://leetcode.com/problems/string-to-integer-atoi/description/) +- [Number of Islands - LeetCode](https://leetcode.com/problems/number-of-islands/description/) diff --git a/560_subarray_sum_equals_k_medium/step1.py b/560_subarray_sum_equals_k_medium/step1.py new file mode 100644 index 0000000..c8ee560 --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step1.py @@ -0,0 +1,28 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + num_subarrays = 0 + # cumsums[i] = sum(nums[:i]) + # sum(nums[i:j]) = cumsums[i] - cumsums[j] + cumsums = [0] * (len(nums) + 1) + for i in range(len(nums)): + cumsums[i + 1] = cumsums[i] + nums[i] + + for i in range(len(nums)): + for j in range(i + 1, len(nums) + 1): + sum_from_i_to_j = cumsums[j] - cumsums[i] + if sum_from_i_to_j == k: + num_subarrays += 1 + + return num_subarrays + + +# @lc code=end diff --git a/560_subarray_sum_equals_k_medium/step1_hashmap.py b/560_subarray_sum_equals_k_medium/step1_hashmap.py new file mode 100644 index 0000000..26aac12 --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step1_hashmap.py @@ -0,0 +1,28 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + # k = nums[i:j] = cumsums[j] - cumsums[i] + # if cumsums[j]-k in cumsum_hashmap for j in i+1, ..., then OK + num_subarrays = 0 + # cumsums[i] = sum(nums[:i]) + cumsums = [0] * (len(nums) + 1) + for i in range(len(nums)): + cumsums[i + 1] = cumsums[i] + nums[i] + + hashmap: dict[int, int] = defaultdict(int) + for i in range(len(nums) + 1): + num_subarrays += hashmap[cumsums[i] - k] + hashmap[cumsums[i]] += 1 + return num_subarrays + + +# @lc code=end diff --git a/560_subarray_sum_equals_k_medium/step2.py b/560_subarray_sum_equals_k_medium/step2.py new file mode 100644 index 0000000..4483fde --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step2.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + num_subarrays = 0 + for i in range(len(nums)): + subarray_sum = 0 # sum(nums[i:j+1]) + for j in range(i, len(nums)): + subarray_sum += nums[j] + if subarray_sum == k: + num_subarrays += 1 + return num_subarrays + + +# @lc code=end diff --git a/560_subarray_sum_equals_k_medium/step2_hashmap.py b/560_subarray_sum_equals_k_medium/step2_hashmap.py new file mode 100644 index 0000000..1529f4c --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step2_hashmap.py @@ -0,0 +1,25 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + # find the number of pairs of (i,j) s.t. 0<=i Date: Sun, 21 Sep 2025 19:36:59 +0900 Subject: [PATCH 2/8] Add step3 file (no implementations) --- 560_subarray_sum_equals_k_medium/step3.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 560_subarray_sum_equals_k_medium/step3.py diff --git a/560_subarray_sum_equals_k_medium/step3.py b/560_subarray_sum_equals_k_medium/step3.py new file mode 100644 index 0000000..40ae2dd --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step3.py @@ -0,0 +1,13 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + return 0 + + +# @lc code=end From d77684974b0368c9c3999462e2db4b7227733e87 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 21 Sep 2025 19:52:47 +0900 Subject: [PATCH 3/8] Implement two-pointer solution --- .../two_pointers.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 560_subarray_sum_equals_k_medium/two_pointers.py diff --git a/560_subarray_sum_equals_k_medium/two_pointers.py b/560_subarray_sum_equals_k_medium/two_pointers.py new file mode 100644 index 0000000..219721f --- /dev/null +++ b/560_subarray_sum_equals_k_medium/two_pointers.py @@ -0,0 +1,29 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# +# @lc code=start +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + Find the subarraySum in O(n) when nums have positive elements. + """ + if not nums: + return 0 + num_subarrays = 0 + subarray_sum = 0 + left = 0 + for right in range(len(nums)): + subarray_sum += nums[right] + while left < right and subarray_sum > k: + subarray_sum -= nums[left] + left += 1 + + if subarray_sum == k: + num_subarrays += 1 + + return num_subarrays + + +# @lc code=end From e65b6ce3bda8e18148caef213066565aa9052c31 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 21 Sep 2025 19:54:53 +0900 Subject: [PATCH 4/8] Update README to add two-pointer solution and improve hashmap explanation --- 560_subarray_sum_equals_k_medium/README.md | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/560_subarray_sum_equals_k_medium/README.md b/560_subarray_sum_equals_k_medium/README.md index 3a75e88..4e56b73 100644 --- a/560_subarray_sum_equals_k_medium/README.md +++ b/560_subarray_sum_equals_k_medium/README.md @@ -78,7 +78,8 @@ class Solution: # 別解・模範解答 -ハッシュマップを使う方法。時間計算量を`O(n)`にできる。 +## ハッシュマップを使う方法 +時間計算量を`O(n)`にできる。 ```python from collections import defaultdict @@ -107,6 +108,32 @@ class Solution: - 時間計算量:`O(1)` - 空間計算量:`O(n)` +## `nums`が正の数のみを含む場合(two pointers) +```python +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + Find the subarraySum in O(n) when nums have positive elements. + """ + if not nums: + return 0 + num_subarrays = 0 + subarray_sum = 0 + left = 0 + for right in range(len(nums)): + subarray_sum += nums[right] + while left < right and subarray_sum > k: + subarray_sum -= nums[left] + left += 1 + + if subarray_sum == k: + num_subarrays += 1 + + return num_subarrays +``` +- ただし、`nums`に0が含まれる場合は、結構複雑になる + + # 想定されるフォローアップ質問 ## CS 基礎 From 18c1dca9fd066dcdd9799540db78de6685a37d32 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 9 Nov 2025 15:59:38 +0900 Subject: [PATCH 5/8] Implement solution for Subarray Sum Equals K problem --- 560_subarray_sum_equals_k_medium/step3_1.py | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 560_subarray_sum_equals_k_medium/step3_1.py diff --git a/560_subarray_sum_equals_k_medium/step3_1.py b/560_subarray_sum_equals_k_medium/step3_1.py new file mode 100644 index 0000000..aee33a8 --- /dev/null +++ b/560_subarray_sum_equals_k_medium/step3_1.py @@ -0,0 +1,33 @@ +# +# @lc app=leetcode id=560 lang=python3 +# +# [560] Subarray Sum Equals K +# + +# @lc code=start +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + Find the number of pairs (i,j) such that sum(nums[i:j]) == k (0<=i count + subarray_sum_count: dict[int, int] = defaultdict(int) + sum_to_j = 0 + # Add sum(nums[:0]) = 0 + subarray_sum_count[0] = 1 + for j in range(1, len(nums) + 1): + sum_to_j += nums[j - 1] # sum(nums[:j]) + count = subarray_sum_count[sum_to_j - k] + num_subarrays += count + subarray_sum_count[sum_to_j] += 1 + return num_subarrays + + +# @lc code=end From 370a0d0731e9914190b3c8caf67707db1e5512da Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 9 Nov 2025 16:33:08 +0900 Subject: [PATCH 6/8] Update README and step3_1.py to clarify subarray sum solution and improve documentation --- 560_subarray_sum_equals_k_medium/README.md | 46 +++++++++++++++++++++ 560_subarray_sum_equals_k_medium/step3_1.py | 6 +-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/560_subarray_sum_equals_k_medium/README.md b/560_subarray_sum_equals_k_medium/README.md index 4e56b73..a62ba6c 100644 --- a/560_subarray_sum_equals_k_medium/README.md +++ b/560_subarray_sum_equals_k_medium/README.md @@ -73,6 +73,52 @@ class Solution: ## step3 +`step3_1.py` (20 min) +```python +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + Find the number of pairs (i,j) such that sum(nums[i:j]) == k (0<=i count + subarray_sum_count: dict[int, int] = defaultdict(int) + sum_to_j = 0 + # Add sum(nums[:0]) = 0 + subarray_sum_count[0] = 1 + for j in range(1, len(nums) + 1): + sum_to_j += nums[j - 1] # sum(nums[:j]) + count = subarray_sum_count[sum_to_j - k] + num_subarrays += count + subarray_sum_count[sum_to_j] += 1 + return num_subarrays +``` +- `sum_to_j`は`prefix_sum`の方が適切かも +- `subarray_sum_count` も `prefix_sum_count` の方が適切かも + +テストケース + + +- 単一要素:`nums=[1]`, `k=1` -> `1`、`nums=[1]`, `k=0` -> `0` +- 同じ要素が複数ある: `nums=[1, 1, 1]`, `k=3` -> `1`、`nums=[1, 1, 1]`, `k=2` -> `2` +- 標準的なケース: `nums=[1, 2, 3, -3, 3]`, `k=3` -> `4` +- k=0: `nums=[1, -1, 1, -1]`, `k=0` -> `4` +- 0を多く含む: `nums=[0, 1, 0]`, `k=1` -> `4` + - 足し算では、0が(単位元なので)特殊なケースになることが多い + - 掛け算では、1 +- kが負: `nums=[-1, -1, -1]`, `k=-2` -> `2` +- 空配列: `nums=[]`, `k=0` -> `0`、`nums=[]`, `k=1` -> `0` + ## step4 (FB) diff --git a/560_subarray_sum_equals_k_medium/step3_1.py b/560_subarray_sum_equals_k_medium/step3_1.py index aee33a8..c6e79f5 100644 --- a/560_subarray_sum_equals_k_medium/step3_1.py +++ b/560_subarray_sum_equals_k_medium/step3_1.py @@ -11,10 +11,8 @@ class Solution: def subarraySum(self, nums: list[int], k: int) -> int: """ - Find the number of pairs (i,j) such that sum(nums[i:j]) == k (0<=i count From 2c513812fe2142ff495f9664c34ceba210828ed0 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 9 Nov 2025 16:38:57 +0900 Subject: [PATCH 7/8] Update README to add follow-up question on space complexity and memory optimization suggestion --- 560_subarray_sum_equals_k_medium/README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/560_subarray_sum_equals_k_medium/README.md b/560_subarray_sum_equals_k_medium/README.md index a62ba6c..bc96b51 100644 --- a/560_subarray_sum_equals_k_medium/README.md +++ b/560_subarray_sum_equals_k_medium/README.md @@ -181,12 +181,8 @@ class Solution: # 想定されるフォローアップ質問 - -## CS 基礎 - -## システム設計 - -## その他 +- この実装の最悪空間使用量は?どうすれば実使用メモリを抑えられますか? + - `defaultdict`の代わりに`dict.get`を使えば、メモリ使用量を抑えられる可能性がある。 # 次に解く問題の予告 - [String to Integer (atoi) - LeetCode](https://leetcode.com/problems/string-to-integer-atoi/description/) From e0eec1ca2efb0dcc5b4fe34c076c405a2ec17614 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Mon, 10 Nov 2025 18:56:10 +0900 Subject: [PATCH 8/8] Add step3 --- 560_subarray_sum_equals_k_medium/README.md | 24 ++++++++++++++++------ 560_subarray_sum_equals_k_medium/step3.py | 12 ++++++++++- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/560_subarray_sum_equals_k_medium/README.md b/560_subarray_sum_equals_k_medium/README.md index bc96b51..768e9e7 100644 --- a/560_subarray_sum_equals_k_medium/README.md +++ b/560_subarray_sum_equals_k_medium/README.md @@ -103,12 +103,6 @@ class Solution: - `subarray_sum_count` も `prefix_sum_count` の方が適切かも テストケース - - - 単一要素:`nums=[1]`, `k=1` -> `1`、`nums=[1]`, `k=0` -> `0` - 同じ要素が複数ある: `nums=[1, 1, 1]`, `k=3` -> `1`、`nums=[1, 1, 1]`, `k=2` -> `2` - 標準的なケース: `nums=[1, 2, 3, -3, 3]`, `k=3` -> `4` @@ -119,6 +113,24 @@ subarraySum([1, -1, 1, -1], 0) == 4 --> - kが負: `nums=[-1, -1, -1]`, `k=-2` -> `2` - 空配列: `nums=[]`, `k=0` -> `0`、`nums=[]`, `k=1` -> `0` + +```python +from collections import defaultdict +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + num_subarrays = 0 + prefix_sum_counts = defaultdict(int) + prefix_sum = 0 + for index in range(len(nums)+1): + if index > 0: + prefix_sum += nums[index-1] + count_end_with_index = prefix_sum_counts[prefix_sum-k] + num_subarrays += count_end_with_index + prefix_sum_counts[prefix_sum] += 1 + + return num_subarrays +``` + ## step4 (FB) diff --git a/560_subarray_sum_equals_k_medium/step3.py b/560_subarray_sum_equals_k_medium/step3.py index 40ae2dd..f94228d 100644 --- a/560_subarray_sum_equals_k_medium/step3.py +++ b/560_subarray_sum_equals_k_medium/step3.py @@ -5,9 +5,19 @@ # # @lc code=start +from collections import defaultdict class Solution: def subarraySum(self, nums: list[int], k: int) -> int: - return 0 + num_subarrays = 0 + prefix_sum_counts = defaultdict(int) + prefix_sum = 0 + for index in range(len(nums)+1): + if index > 0: + prefix_sum += nums[index-1] + count_end_with_index = prefix_sum_counts[prefix_sum-k] + num_subarrays += count_end_with_index + prefix_sum_counts[prefix_sum] += 1 + return num_subarrays # @lc code=end