Skip to content
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
154 changes: 154 additions & 0 deletions 209_minimum-size-subarray-sum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# 209. Minimum Size Subarray Sum

## 1st

### ①

右端をループで回しながら、subarray_sum < target となるまで左端を進める、を繰り返す。

所要時間: 8:15

n: len(nums)
- 時間計算量: O(n)
- 空間計算量: O(n)

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
assert target > 0
left = -1 # exclusive
subarray_sum = 0
min_length = len(nums) + 1
for right in range(len(nums)):
subarray_sum += nums[right]
while subarray_sum >= target:
min_length = min(min_length, right - left)
left += 1
subarray_sum -= nums[left]
if min_length == len(nums) + 1:
return 0
Comment on lines +28 to +29

Choose a reason for hiding this comment

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

このケースは解が存在していないケースかと思います。この問題における解なしというのは sum(nums) < target のケースなので、この関数の一番始めでチェックしておけばそれ以降は解が存在する前提になり複雑度が減りそうです。

またここで判断するケースでも len(nums) + 1 が解なしを意味するというのはぱっと見ではわかりにくいので、math.inf など明らかにおかしいもので初期化しておくとよさそうに思いました。(上記に書いたように解が存在していることをチェックした後であれば min_length = len(nums) のような解となりうる値の中の最大値で初期化するとかもいいかと思います)

Copy link
Owner Author

Choose a reason for hiding this comment

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

math.inf, 型がfloatなのがちょっと気になるんですよね...使うメリットはあるので選択肢としてありだとは思いますが。
sum(nums) < target かを先にチェックするのは、それも手だと思いますがそこでO(n)かかるコストを払うトレードオフをどう見るかですね... (cpythonなので) C実装のsumは速いからいいだろうという考えもまた分かりますが。

Choose a reason for hiding this comment

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

math.inf, 型がfloatなのがちょっと気になるんですよね...使うメリットはあるので選択肢としてありだとは思いますが。

なるほど、たしかに型が異なるのは気になるポイントかもですね。

sum(nums) < target かを先にチェックするのは、それも手だと思いますがそこでO(n)かかるコストを払うトレードオフをどう見るかですね... (cpythonなので) C実装のsumは速いからいいだろうという考えもまた分かりますが。

全体の時間計算量 O(n) を悪化させる修正ではないので、個人的には可読性を優先するかなというコメントでした。(書かれているとおりトレードオフなので、可読性の向上があまりないと感じていたり、少しでも計算を早めたいという状況であれば、選択は変わると思います)

return min_length
```

### ②

左端をループで回すパターン。

所要時間: 16:45

n: len(nums)
- 時間計算量: O(n)
- 空間計算量: O(n)

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
min_length = len(nums) + 1
subarray_sum = 0
end = 0
for begin in range(len(nums)):
while end < len(nums) and subarray_sum < target:
subarray_sum += nums[end]
end += 1
if subarray_sum >= target:
min_length = min(min_length, end - begin)
subarray_sum -= nums[begin]
Comment on lines +53 to +55

Choose a reason for hiding this comment

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

end == len(nums) の場合に begin を回すための処理かなと思いました。
だとすると、subarray_sum < target してたらもう subarray_sum は小さくなる一方だと思うので、もう引かなくてもいいのかなと思いました。

            if subarray_sum < target:
                break
            min_length = min(min_length, end - begin)
            subarray_sum -= nums[begin]

ただ、begin のループを抜けようとしたらもうひと手間いると思うので、少し流れが違うだけですね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

たしかにそう書けますね。ありがとうございます!

if min_length == len(nums) + 1:
return 0
return min_length
```

## 2nd

### 参考

- https://discord.com/channels/1084280443945353267/1233603535862628432/1264033965724012656
- https://github.com/goto-untrapped/Arai60/pull/40

累積和を使ってもよい。

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
assert target > 0
cumsum = [0]
for num in nums:
cumsum.append(cumsum[-1] + num)
begin = 0
min_length = len(nums) + 1
for end in range(len(cumsum)):
while cumsum[end] - cumsum[begin] >= target:
min_length = min(min_length, end - begin)
begin += 1
Comment on lines +79 to +82

Choose a reason for hiding this comment

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

個人的に二重ループになっていると少し複雑かなと思いました。if 文を使って leftright を順に進めていくようにするとループ自体は1つなのでわかりやすいかもです。またリーダブルコードという本には beginend は 開閉区間([begin, end))の場合に用いるというような記述があったので、誤解を生む可能性があるかもです。(ここは所属組織の文化や慣習が大きいと思いますが)

Suggested change
for end in range(len(cumsum)):
while cumsum[end] - cumsum[begin] >= target:
min_length = min(min_length, end - begin)
begin += 1
left = 0
right = 1
while right < len(cumsum):
if cumsum[right] - cumsum[left] < target:
right += 1
else:
min_size = min(min_size, right - left)
left += 1

if min_length == len(nums) + 1:
return 0
return min_length
```

- https://discord.com/channels/1084280443945353267/1225849404037009609/1253024249040474258
- https://github.com/SuperHotDogCat/coding-interview/pull/31
- https://discord.com/channels/1084280443945353267/1196472827457589338/1246777474927562752
- https://github.com/Mike0121/LeetCode/pull/22

累積和と二分探索。

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
cumsum = [0]

Choose a reason for hiding this comment

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

リストなので、cumsums の方が分かりやすいのかなと思いました。
cumsum にした方がいい理由が思い当たりませんでした。)

for num in nums:
cumsum.append(cumsum[-1] + num)

Choose a reason for hiding this comment

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

累積和を求めるときのこの書き方良いですね、参考にします。
自分が書くときのcumsum[i + 1] = nums[i] + cumsum[i]より見やすいと思いました。

min_length = len(nums) + 1
for i in range(len(cumsum)):
j = bisect_left(cumsum, cumsum[i] + target)

Choose a reason for hiding this comment

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

i, j はもう少し変数名こだわっても良いかと思いました。
i を start_index (or start), jがend_index (or end)とかですかね?(ちょっと自信ないですが。)

if j < len(cumsum):
min_length = min(min_length, j - i)
if min_length == len(nums) + 1:
return 0
return min_length
```

- https://discord.com/channels/1084280443945353267/1201211204547383386/1228603635693387808
- https://github.com/shining-ai/leetcode/pull/49
- https://discord.com/channels/1084280443945353267/1200089668901937312/1219219869510140005
- https://github.com/hayashi-ay/leetcode/pull/51

全探索。TLE。

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
min_length = len(nums) + 1
for begin in range(len(nums)):
subarray_sum = 0
end = begin
while end < len(nums) and subarray_sum < target:
subarray_sum += nums[end]
end += 1
if subarray_sum >= target:
min_length = min(min_length, end - begin)
if min_length == len(nums) + 1:
return 0
return min_length
```


## 3rd

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
assert target > 0
min_length = len(nums) + 1
subarray_sum = 0
left = -1 # exclusive

Choose a reason for hiding this comment

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

感想: indexに-1設定できると左の開区間を表現しやすくていいですね

(普段書いているRustだとこれがとってもやりにくい)

for right in range(len(nums)):
subarray_sum += nums[right]
while subarray_sum >= target:
min_length = min(min_length, right - left)
left += 1
subarray_sum -= nums[left]
if min_length == len(nums) + 1:
return 0
return min_length
```