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
97 changes: 97 additions & 0 deletions problem51/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
## 取り組み方
- step1: 5分以内に空で書いてAcceptedされるまで解く + テストケースと関連する知識を連想してみる
- step2: コードを整える + 他の妥当な実装があれば実装してみる
- step3: 10分以内に1回もエラーを出さずに3回連続で解く

## step1
subarrayの先頭window_startと末尾window_end、subarrayに含まれるnumの和window_sumを管理しながら、和がtarget以上になる最小の長さを探す。

具体的には、window_endを1ずつ進めていくループの中で、window_sumがtarget以上になったらtarget未満になるまでwindow_startを1ずつ進めるような操作と最小の長さを更新するのを繰り返す。

時間計算量はO(nums.length)なので数百m秒程度で処理が完了する見込み。

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

Choose a reason for hiding this comment

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

math.inf とするか、from math import inf あたりはあっていいかなと思いました


for window_end, num in enumerate(nums):
window_sum += num
Comment on lines +20 to +21

Choose a reason for hiding this comment

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

Nit: これは完全な好みなのですが、あとで nums[window_start] と index でアクセスしているのもあって、window_sum += nums[window_end] の方が統一感と、end の要素にアクセスしている感じがあって好きです。

while window_sum >= target:
min_len = min(min_len, window_end - window_start + 1)
window_sum -= nums[window_start]
window_start += 1

if isinf(min_len):

Choose a reason for hiding this comment

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

これ私も意図的によくやりますし、どちらがいいとは言いづらいのですが、isinf って -inf ともマッチするんですよね。min_len == inf の方が一応厳密ではあるかと思いますがこのくらいのサイズのプログラムならどちらでもいいかもしれません。
https://docs.python.org/3/library/math.html#math.isinf

Copy link
Owner Author

Choose a reason for hiding this comment

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

-infもTrueと返してしまうこと知りませんでした。コメントありがとうございます。

Return True if x is a positive or negative infinity,
https://docs.python.org/3/library/math.html#math.isinf

であれば、min_len == infがいいですね。

return 0
return min_len
```

## step2
### 読んだ
https://github.com/fhiyo/leetcode/pull/49/files
https://github.com/shining-ai/leetcode/pull/49/files
https://github.com/hroc135/leetcode/pull/46/files

### 感想
- infがfloat型なのが微妙(と思いながら書いてしまった)
- 以下の解決策がよいと思った
> 一つ考えたのは、target <= sum(nums) を確認すれば、min_length = len(nums) がいえますね。
https://github.com/shining-ai/leetcode/pull/49/files#r1563843030

- 以下を読んでいて、そもそもこの関数の用途はどんなものかを考える工程が40問目をやっていた時期より甘くなっていた気がするので反省

> 計算量はあくまでも極限を取ったときの振る舞いなので、実際に大事なのは、用途から考えられる状況において「何秒」で実行できるかの見積もりです。「用途(目的)」と「何秒(価値)」です。
https://github.com/Hurukawa2121/leetcode/pull/1#discussion_r1869920625

- 「連続した要素の合計が、ある条件を満たす最小の区間を見つける」というシナリオなので、
- ユースケースは
- 在庫管理やデータ分析あたりだろうか
- 連続したメモリブロックの回収とかでも使われてそう
- 今後を見据えてケアしておかないといけないのは
- numsに負の数が含まれる
- これは今回のアルゴリズムだと厳しそう
- numsに浮動小数点が含まれる

```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target:
return 0

min_len = sum(nums)
window_sum = 0
window_start = 0

for window_end, num in enumerate(nums):
window_sum += num
while window_sum >= target:
min_len = min(min_len, window_end - window_start + 1)
window_sum -= nums[window_start]
window_start += 1

return min_len
```

## step3
```py
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if sum(nums) < target:

Choose a reason for hiding this comment

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

これ必要ですか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

レビューありがとうございます。

自分は必要だと認識しています。
前提として、min_len = min(min_len, x)target以上を作れるsubarrayの最小長を取得しております。
ここでmin_lenの初期値について考えると、minをとってmin_lenを更新しているために、
min_lenとして更新されうる値の最大より大きい値を設定しておく必要があります。
また、min_lenが一度も更新されなかった場合は、target以上を作れないということで、0を返す必要があります。

というわけで、一番初めに「すべてのnumsを足してもtarget以上を作れない(=min_lenが一度も更新されない)」ケースを弾いておくことで、すべてのnumsを足した数をmin_lenの初期値として扱っているというのが意図です。

上記の認識が違う、あるいは違和感を持った点が解消されていないようでしたら、
具体的な箇所をご指摘お願いします。

return 0

min_len = sum(nums)

Choose a reason for hiding this comment

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

上のコメントにも関連するのですが、「取りうる最大の長さよりも大きい」という意味合いなら、len(nums) を取ったほうが自然な気がします。
今回の制約からは外れますが、0 < x < 1 の float を取るような入力が来ると動かないと思います。

ただその場合 sum(nums) == target になるようなケースを処理できないので

if sum(nums) < target:
  return 0
if sum(nums) == target:
  return len(nums)
...

とするか、もしくは min_len = len(nums) + 1 と初期化しても良いと思います。この場合はちょっとコメントでも欲しくなりますね。
あるいは、型の違いはあれど math.inf でもよいと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

レビューありがとうございます。

上のコメントにも関連するのですが、「取りうる最大の長さよりも大きい」という意味合いなら、len(nums) を取ったほうが自然な気がします。

これ今気づきました。おっしゃる通りですね。

今回の制約からは外れますが、0 < x < 1 の float を取るような入力が来ると動かないと思います。

おっしゃる通りですね。
min_lenの初期化にnumsの値を使ってしまっていることがそもそもよくなかったので、
min_len = len(nums) + 1がよいかと思いました。ご提案ありがとうございます。

window_sum = 0
start = 0

for end, num in enumerate(nums):
window_sum += num
while window_sum >= target:
min_len = min(min_len, end - start + 1)
window_sum -= nums[start]
start += 1

return min_len
```