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
86 changes: 86 additions & 0 deletions problem31/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
## 取り組み方
- step1: 5分以内に空で書いてAcceptedされるまで解く + テストケースと関連する知識を連想してみる
- step2: 他の方の記録を読んで連想すべき知識や実装を把握した上で、前提を置いた状態で最適な手法を選択し実装する
- step3: 10分以内に1回もエラーを出さずに3回連続で解く

## step1
`nums[index]`を使用したときのLISを`length_LIS[index]`とする

ある`index`とその`index`よりも前の`former_index`について、
`nums[former_index] < nums[index]`の時、`length_LIS[former_index] + 1`が`length_LIS[index]`の候補となる。
これを一通りみていって、`length_LIS`を更新していって最大のものが答えとなる。

```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
length_nums = len(nums)
length_LIS = [1] * length_nums

for index in range(1, length_nums):
for former_index in range(index):
if nums[index] > nums[former_index]:
length_LIS[index] = max(
length_LIS[index],
length_LIS[former_index] + 1
)
return max(length_LIS)
```

## step2
### 読んだコード
- https://github.com/fuga-98/arai60/pull/31/files
- https://github.com/sakupan102/arai60-practice/pull/32/files
- https://github.com/hayashi-ay/leetcode/pull/27/files

### 感想
- numsをはじめから見ていって、各長さのLISにおける末尾の最小値を保持するやり方もあった
- 発想も素直な気がするのでこれは思いついても良さそうだった
- ついでに`bisect_left`の実装をみたが、変数名が雑すぎるなあ
- https://github.com/python/cpython/blob/3.13/Lib/bisect.py#L74
Copy link

Choose a reason for hiding this comment

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

docstringをきちんと書いているからOKなんですかね。

- 一方、末尾の最小値を保持していくやり方は、最終的なLIS自体を返してくれと追加の要件が出てきたときに編集しにくそうなので、動的計画法で実装する方が良さそう
- `segment tree`と座標圧縮を利用した解法もあるみたいだが、常識から外れそうなので一旦スキップする
- `LIS`は小文字でも良さそう

#### 動的計画法
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
length_lis = [1] * len(nums)
for index in range(len(nums)):
for former_index in range(index):
if nums[index] > nums[former_index]:
length_lis[index] = max(
length_lis[index],
length_lis[former_index] + 1
)
return max(length_lis)
```

#### 二分探索
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
# tails: 各長さの増加部分列における最小の末尾値を保持するリスト
tails = []
for num in nums:
insertion_position = bisect_left(tails, num)
if insertion_position == len(tails):
tails.append(num)
else:
tails[insertion_position] = num
Comment on lines +67 to +70
Copy link

Choose a reason for hiding this comment

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

私は、唐突にこれを逆にしたほうが素直かなと思いましたが

if insertion_position < len(tails):
    tails[insertion_position] = num
else:
    tails.append(num)

趣味の範囲です。

Copy link
Owner Author

Choose a reason for hiding this comment

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

レビューありがとうございます。
確かに「収まっている場合->収まっていない場合」となっていた方が自然ですね。

return len(tails)
```

## step3

```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
length_lis = [1] * len(nums)
for index in range(len(nums)):
for former_index in range(index):
Copy link

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.

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

見返してみると、
階層が深くなっているので関数化するのもありな気がしてきました。

if nums[index] > nums[former_index]:

Choose a reason for hiding this comment

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

ネスト深めなので、早期continueしたい気もします。

Copy link
Owner Author

Choose a reason for hiding this comment

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

すみません。コメント見逃しておりました。
自分も、コード見返していたところ、同じ感想を持ちました。

length_lis[index] = max(
length_lis[index], length_lis[former_index] + 1)
return max(length_lis)
```