Skip to content

Create 560. Subarray Sum Equals K.md #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions 競技プロ就活部PR用/560. Subarray Sum Equals K.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
累積和に関する議論
```
累積和を日常で見る機会ってあると思うんですよ。
たとえばですけれども、電車の各駅の距離とかかる時間が書かれていて、ちょうど10分かかる駅の組み合わせはどれか、といわれたら、
(これはマイナスが出ないので更に楽ですが、)終着駅から出発する電車が、どこを何時何分に通過するかを書き出しながら、
その10分前に別の駅にいたかを確認したらいいですよね。

マイナスが出るようにするために、標高差とかにします? つまり、各駅間の標高差が書かれていて、ちょうど標高差が 100 m な駅の組み合わせを知りたい。
問題は、駅の組み合わせの数ですが、とりあえず、駅の組み合わせを全部列挙してみましょうか。そうすると、multimap でも使いますか。

駅A → 駅B: 5分
駅B → 駅C: 3分
駅C → 駅D: 2分
駅D → 駅E: 1分
駅E → 駅F: 4分

累積和: [0, 5, 8, 10, 11, 15] ([A->A, A->B, A->C, A->D, A->E, A->F])
この場合、10と0があるので、駅Aから駅Dまでで10分、
この場合、15と5があるので、駅Bから駅Fまでで10分
```

```
i番目までの累積和とj番目までの累積和が等しい場合その間[i, j)の要素の和は0となる。
prefixsum[i] = prefixsum[j] (i < j)の時、
prefixsum[i] = nums[0] + nums[1] + nums[2] + nums[3]... + nums[i - 1]
prefixsum[j] = nums[0] + nums[1] + nums[2] + nums[3]... + nums[i - 1] **+ nums[i] ... + nums[j - 1]**
許通部分を引き算して、
nums[i] + nums[i + 1] + num[i + 2] ... + nums[j - 1] = 0
```

## two-pointersによる解法 (×)
### 0回目 v1
時間計算量: O(N^2)<br>
空間計算量: O(1)<br>
> はじめに思いついた解法として、slidingwindowの様に、値がtarget以上になった場合、
> windowを狭めていく解法を思いついた。Description上のテストケースは通過したが、
> numsの値にnegativeが入るとエラーが出ることを考慮しておらず大反省。


```python
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
count = 0
prefix_sum = 0
left = 0
for num in nums:
prefix_sum += num

while prefix_sum >= k:
if prefix_sum == k:
count += 1
prefix_sum -= nums[left]
left += 1
return count
```


## 二重ループによる解法 (TLE)
### 0回目 v2
時間計算量: O(N^2)<br>
空間計算量: O(1)<br>
> 次に最適解が思いつかなかったため、二重ループで解いたがTLE。

```python
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
subarray_count = 0
for i in range(len(nums)):
total = 0
for j in range(i, len(nums)):
total += nums[j]
if total == k:
subarray_count += 1

return subarray_count
```

## HashMapによる解法
### 1回目
時間計算量: O(N)<br>
空間計算量: O(N)<br>
> 以前の自身の解法をもとに、累積和を使って解いた。
```python
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
sum_to_count = defaultdict(int)
sum_to_count[0] = 1
subarray_count = 0
prefix_sum = 0

for num in nums:
prefix_sum += num
if prefix_sum - k in sum_to_count:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subarray_count += sum_to_count[prefix_sum - k]
sum_to_count[prefix_sum] += 1

return subarray_count
```

### 2回目
時間計算量:1回目と同じ <br>
空間計算量:1回目と同じ <br>
sum_to_countをprefix_sum_to_countに変更。

```python
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_sum = 0
prefix_sum_to_count = defaultdict(int)
prefix_sum_to_count[0] = 1
subarray_count = 0

for num in nums:
prefix_sum += num
if prefix_sum - k in prefix_sum_to_count:
subarray_count += prefix_sum_to_count[prefix_sum - k]
prefix_sum_to_count[prefix_sum] += 1

return subarray_count
```

### 3回目
時間計算量:1回目と同じ <br>
空間計算量:1回目と同じ <br>
2回目と同じ。
```python

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かいですが、from collections import defaultdictから書いてあると、親切かなと思います。
(レビューの際、コードを実行して確認したりするケースもあるので)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、そこは考慮できていませんでした。できるだけ書く様にします。

class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_sum = 0
prefix_sum_to_count = defaultdict(int)
prefix_sum_to_count[0] = 1
subarray_count = 0

for num in nums:
prefix_sum += num
if prefix_sum - k in prefix_sum_to_count:
Copy link

@tshimosake tshimosake Jun 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultdict を使っているので、この2行は単に(if 文は省略して) subarray_count += prefix_sum_to_count[prefix_sum - k] と書けます。

追記:「この2行」とは

if prefix_sum - k in prefix_sum_to_count:
    subarray_count += prefix_sum_to_count[prefix_sum - k]

のことです。github上だとどこを指しているのかわかりにくかったので追記しました🙏

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tshimosake さん、 @SuperHotDogCat さん
ありがとうございます。if文確かに不要でした。

subarray_count += prefix_sum_to_count[prefix_sum - k]
prefix_sum_to_count[prefix_sum] += 1

return subarray_count
```