-
Notifications
You must be signed in to change notification settings - Fork 0
300 longest increasing subsequence medium #24
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
base: main
Are you sure you want to change the base?
Conversation
| tails.append(num) | ||
| continue | ||
| tails[index] = min(tails[index], num) | ||
| return len(tails) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
読みやすいと思います。
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| left_max_lengths = [1] * len(nums) | ||
| for i in range(len(nums)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
参考までに、このような書き方もあると思いました。
for i in range(len(nums)):
left_max_lengths[i] = max(
[left_max_lengths[j] + 1 for j in range(i) if nums[j] < nums[i]],
default=1
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
レビューありがとうございます。
確かにその書き方も明瞭ですね。参考にさせていただきます。
| # @lc code=start | ||
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| left_max_lengths = [1] * len(nums) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftだけだと少しわかりづらいかもしれないと思いました。個人的には、ending_index_to_LIS_lengthぐらいにしても良いと思いました。
このツリーが少し参考になるかもしれません。
https://github.com/h1rosaka/arai60/pull/33/files#r2312432424
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そうですね、このあたりの変数名は自分でもなかなか良いのが思いつかず、難しく感じていました。ツリーもありがとうございます。ending_index_to_LIS_length, end_index_to_lengthあたりが良いなと感じました。
| return 0 | ||
| min_tails = [nums[0]] | ||
| for num in nums[1:]: | ||
| j = bisect_left(min_tails, num) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好みだと思いますが、インデックスという意味ならここは i を使うかなと思いました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
レビューありがとうございます。おっしゃる通り、jよりもi、更にindex_to_insertがよりベターですね。
| return 0 | ||
| subsequence_min_tails = [] | ||
| for num in nums: | ||
| index_to_insert = bisect.bisect_left(subsequence_min_tails, num) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一文字変数より index_to_insert の方が読みやすく感じました。
| if array[-1] < x: | ||
| return len(array) | ||
| left = -1 | ||
| right = len(array) - 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right = len(array)でないと、 len(array) が返らず、下の min_tails.append(num) のコードパスを通らないように思いました。読み間違いでしたら申し訳ありません。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確かにその通りですね。ご指摘ありがとうございます。
配列のどの要素よりも大きい値を渡した時に、bisect_leftはlen(nums)を返すべきですが、それを返せていませんね。修正します。
| return max(max_lengths_so_far) | ||
| ``` | ||
|
|
||
| - 変数名がうまくつけられなかった |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
max_lengths_so_far のことであれば、自分なら index_to_max_length と付けると思います。 list のインデックスから、その値を末尾としたときに最大の長さへのマッピング、というニュアンスをこめています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確かにわかりやすいと感じました。max_length_by_indexやmax_length_ending_at_indexあたりもありかもしれません。
リストや辞書の命名に詰まったら*s(複数形)と、*to*や*by*(マッピング)をどちらも検討してみます。
| if index == len(tails): | ||
| tails.append(num) | ||
| continue | ||
| tails[index] = min(tails[index], num) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
勘違いだったら申し訳ないのですが、ここは上でも書かれているように tails[index] = num でもよさそうと思いました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確かにその通りですね。bisect_leftで求めたindexなので、tails[index] >= numが保証されていますね。ご指摘ありがとうございます。
|
読みやすかったです。 |
| `step3_binary_search.py` | ||
| ```python | ||
| class Solution: | ||
| def bisect_left(self, nums: list[int], target: int) -> int: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これでも回るコードになっているので OK と思います。読むときにはこう書いてくれる保証がないので、それよりも広いものを読めるようにしておく必要はあるでしょう。私が読み取る必要があると思っているのは以下です。
- 引数の制約はなにか。
- ループの不変条件はなにか。
- middle の制約はなにか。
- 更新によって不変条件が守られるか。
- ループの終了条件とその時に欲しいものが見つかっているか。
- 有限回で終了するか。
問題へのリンク
300. Longest Increasing Subsequence
言語
Python
自分の解法
step1
numsの要素数をnとすると、O(n^2)O(n)step2
step3
step3_bruteforce.pystep3_binary_search.pytailsは常に昇順にソートされているので、numがtailsのどこに入るかを二分探索。これは覚えていないと書けないleft=-1,right=len(nums)から始めると、mid=-1やmid=len(nums)になるのではないかと不安に思っていたが、while right - left > 1の条件でループするので、ループの中ではright - leftは常に2以上になるので、left < mid < rightが保証される。返り値はrightなので、len(nums)が返ることはある。bisect_rightの実装はstep4 (FB)
別解・模範解答
min_tails_linear.pymin_tails[i]は長さi + 1の増加部分列の最小の末尾要素を表すO(n^2)O(n)numsが昇順にソートされている場合、tailsの長さはnになるmins_tailsは常に昇順にソートされているので、numがmin_tailsのどこに入るかを二分探索で探せる時間計算量はO(log n)になるmin_tails_binary_search_bisect.pyO(log n)O(n)想定されるフォローアップ質問
bisect_leftではなくbisect_rightを使った場合、結果は変わりますか?変わる場合、どのような入力で変わりますか?変わらない場合、その理由は何ですか?bisect_leftとbisect_rightのどちらを使用しても結果は変わらない。なぜなら、求めるLISは"strictly increasing"であり、tails配列に同じ値が存在することはないからである。しかし、もし問題が"non-decreasing"なLISを求めるものであれば、bisect_rightを使用することで、同じ値を持つ要素がtailsに追加される可能性があり、結果が変わることになる。その場合はbisect_rightを使用することで、同じ値を持つ要素がLISに含まれることを許容することになる。次に解く問題の予告