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
156 changes: 156 additions & 0 deletions medium/53/answer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Step1

かかった時間:12min

計算量:nums.length = Nとして

時間計算量:O(N)

空間計算量:O(N)

```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
maximum_subarray_so_far = [0] * len(nums)
maximum_subarray_so_far[0] = nums[0]
for i in range(1, len(nums)):
maximum_subarray_so_far[i] = max(
maximum_subarray_so_far[i - 1] + nums[i],
nums[i]
)

return max(maximum_subarray_so_far)
```

思考ログ:
- 1個前までのmaximum_subarrayから更に伸ばした方がいいか、一旦切るか考える
- 最初```maximum_subarray_so_far[-1]```を反射的に返してしまった
- 配列の要素の意味をちゃんと確認すること
Copy link

Choose a reason for hiding this comment

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

一応、ここの意味を自然言語で説明してくれませんか。
「maximum_subarray_so_far[i] の意味は nums[i] が和として使用する最後の数であった場合、連続する配列の和の最大値」ですね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
変数名の情報だけでは不十分でした。コメントを残すように心がけます。

nums[i]が最後の要素になるような部分列集合を全て考え、各部分列の和をそれぞれ取って、そのうち最大のものを要素とする配列、みたいな感じですかね。日本語が難しい、、

maximum_subarray_so_far[i] = max {Σ_k (A_ij[k])} (∀ j)

A:対象とする部分列の配列
i : nums[i]と対応、対象とする部分列集合の最後の要素 = nums[i]
j : 対象とする部分列集合のインデックス
k : 対象とする部分列の要素のインデックス

Copy link

Choose a reason for hiding this comment

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

あー、いや、コメントが必要だというよりは、言語化しておいたほうが理解が深まるだろうと思ったので書いておいたくらいです。


# Step2

講師役目線でのセルフツッコミポイント:

参考にした過去ログなど:
- https://github.com/seal-azarashi/leetcode/pull/30
- https://github.com/Ryotaro25/leetcode_first60/pull/35
- https://github.com/Yoshiki-Iwasa/Arai60/pull/48
- https://github.com/fhiyo/leetcode/pull/33
- 再帰の実装
- https://github.com/goto-untrapped/Arai60/pull/30
- https://github.com/YukiMichishita/LeetCode/pull/13
- 色々な解放がコンパクトにまとめてあるので復習時に参照したい
- https://github.com/sakupan102/arai60-practice/pull/33
- ```prefix_sum - min_prefix_sum```から```current_sum```を作る(Kadane)
- https://github.com/sakupan102/arai60-practice/pull/33/files#r1611415355
- https://github.com/SuperHotDogCat/coding-interview/pull/14
- 分割統治
- https://github.com/shining-ai/leetcode/pull/33
- 分割統治
- https://github.com/hayashi-ay/leetcode/pull/36
- 分割統治

step1を配列を使わずに
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
maximum_subarray_sum = -sys.maxsize

Choose a reason for hiding this comment

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

sys.maxsizeを使っているのが少し気になりました。他の言語と違ってPythonのintは制限がないので、sys.maxsize以下の値も取りうるのが個人的にうーんと思った点なのかなと思います。まあ何かしら下限値を用意する必要がありはするので、別に問題はないと思います。

あとはnums[0]を初期にして、forループはnums[1:]について回してあげるのもありかなと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

  • 問題の制約から下限値を自分で定義する
  • np.inf

あたりの選択肢で揺れてますが、今回はこれにしました(intにしておきたい、くらいで深い意味はないです)

prefix_sum = 0
for num in nums:
prefix_sum = max(prefix_sum + num, num)
maximum_subarray_sum = max(maximum_subarray_sum, prefix_sum)

return maximum_subarray_sum
```
思考ログ:
- 配列が必要かどうか、は考えておきたい(DPだと反射的に配列を用意しがち)

Choose a reason for hiding this comment

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

DPだと反射的に配列を用意しがちという表現になんかちょっと違和感を感じました。現在の状態の値がひとつ以上前の状態の値によって決まるような問題を解くのに適しているのがDPで、各状態についての値を保存するために配列を用意するという方法がまずあり、工夫すれば配列を用意しないでも書けるくらいの話かなと思います

Copy link
Owner Author

@TORUS0818 TORUS0818 Nov 9, 2024

Choose a reason for hiding this comment

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

DPだと反射的に配列を用意しがち

という自省メモのつもりでした。

ご指摘のとおり、DPの性質上、配列をよく使うという話なのですが、DPだ->配列用意しよう、のような脳死ムーブを(私が)しがちな気がしたので。


再帰
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:

Choose a reason for hiding this comment

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

こちらの解法よりstep1のボトムアップやや累積和がいいと思いました。
関数から2つの値が返ってくることが処理を追い辛くしていると感じました。

一方でこの問題は他に比べて再帰で解くのは難しいと思いました。。。

def max_sub_array_helper(i: int) -> tuple[int]:
if i == 0:
return nums[0], nums[0]

prefix_sum, max_sub_array_sum = max_sub_array_helper(i - 1)
prefix_sum = max(nums[i], prefix_sum + nums[i])
max_sub_array_sum = max(max_sub_array_sum, prefix_sum)
return prefix_sum, max_sub_array_sum

prefix_sum, max_sub_array_sum = max_sub_array_helper(len(nums) - 1)
return max_sub_array_sum
```
思考ログ:
- 以下で実装されていたのでやってみる
- https://github.com/fhiyo/leetcode/pull/33/files
- nonlocalをやめて返り値で渡していく方式にしてみた
- あとで見て気づいたが、ここだけ```sub_array```にしてるの気になる(元の関数はSubArrayだからsub_arrayにしたのだろう)

累積和を求めて全探索(TLE)
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
prefix_sum = [0]
for num in nums:
prefix_sum.append(prefix_sum[-1] + num)

maximum_subarray_sum = nums[0]
for i in range(len(prefix_sum) - 1):
for j in range(i + 1, len(prefix_sum)):
maximum_subarray_sum = max(
maximum_subarray_sum,
prefix_sum[j] - prefix_sum[i]
)

return maximum_subarray_sum
```
思考ログ:
- 計算量的に通らないなと思って実装しなかったもの
- https://discord.com/channels/1084280443945353267/1206101582861697046/1208473290881110117
> どうせ、1番上は思いついたけれども、価値のないものだと思って捨てたでしょう。

今まで見た中で最小のprefix_sumを記録しておく方法
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
min_prefix_sum_so_far = 0
prefix_sum = 0
maximum_subarray_sum = nums[0]
for num in nums:
prefix_sum += num
maximum_subarray_sum = max(
maximum_subarray_sum,
prefix_sum - min_prefix_sum_so_far
)
min_prefix_sum_so_far = min(min_prefix_sum_so_far, prefix_sum)

return maximum_subarray_sum
```
思考ログ:
- 累積和の履歴で最も小さかったもの(谷底)から現在までの距離を測ることで要素和が最大の部分列を探していく

# Step3

かかった時間:2min

```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
maximum_subarray_sum = nums[0]
prefix_sum = 0
for num in nums:
prefix_sum = max(prefix_sum, 0) + num
Copy link

Choose a reason for hiding this comment

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

このアルゴリズムも prefix_sum が「num を配列の最後の値として使うことに決めた場合の、連続する配列の和の最大値」という意味だと思うと理解できますね。

maximum_subarray_sum = max(maximum_subarray_sum, prefix_sum)

return maximum_subarray_sum
```
思考ログ:
- ```prefix_sum```の更新で```num```を外に出してみた

# Step4

```python
```
思考ログ: