-
Notifications
You must be signed in to change notification settings - Fork 0
300 longest increasing subsequence #30
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
Open
MA-yo-TA
wants to merge
3
commits into
main
Choose a base branch
from
300-Longest-Increasing-Subsequence
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| # 300. Longest Increasing Subsequence | ||
|
|
||
| https://leetcode.com/problems/longest-increasing-subsequence/ | ||
|
|
||
| ## step1(まず通す) | ||
|
|
||
| 最初、 | ||
|
|
||
| - `subsequence = []` を用意する | ||
| - リストの要素を前から見ていき、今見ている要素が | ||
| - subsequence の最後の要素より大きいなら末尾につけ加える | ||
| - subsequence の末尾より小さくて、その一つ前(後ろから2番目) より大きいなら subsequence の末尾と今見ている値を入れ替える | ||
|
|
||
| でできそう?と思ったが、`nums = [10,11,12, 1, 2, 3, 4]` で破綻する。(↑だと 3 になるが実際は 4) | ||
|
|
||
| 他で思いついたのは、再帰的なやり方で | ||
|
|
||
| - リストが長さ1ならそのリストがLIS | ||
| - リストの長さが2以上のとき、 | ||
| - もし最後の要素 > 「残りの部分におけるLIS」の最後の要素 → 「」と最後の要素を結合したものがLIS | ||
| - そうでないなら、「残りの部分におけるLIS」がもとの配列自体のLIS | ||
|
|
||
| ただこれも最初のものと同じで、成り立っていない。 | ||
|
|
||
| ```python | ||
| # 成り立っていない | ||
| class Solution: | ||
| def get_lis_len_and_end_num(self, nums: list[int]) -> tuple[int, int]: | ||
| if len(nums) == 1: | ||
| return 1, nums[0] | ||
|
|
||
| end_num = nums.pop() | ||
| rest_lenght, rest_end = self.get_lis_len_and_end_num(nums) | ||
| if rest_end < end_num: | ||
| return rest_lenght + 1, end_num | ||
| else: | ||
| return rest_lenght, rest_end | ||
|
|
||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| length_of_lis, _ = self.get_lis_len_and_end_num(nums) | ||
| return length_of_lis | ||
| ``` | ||
|
|
||
| 答えを見る。 | ||
|
|
||
| > dp[i] を次のように定義します。 | ||
| > dp[i] = nums[i] を最後の要素とする増加部分列の最大長 | ||
| > nums[i] より前にある要素をすべて確認し、nums[j] < nums[i] なら、nums[j] で終わる部分列の後ろに nums[i] を追加できます。 | ||
| > したがって dp[i] = max(dp[i], dp[j] + 1) となります。 | ||
|
|
||
| 上で考えていた方法は、「残りの部分」を見る時に暫定的な LIS だけを保持して他の情報を捨てていたのがよくなさそう。今一番長い列が将来も有利とは限らない、ということを理解できていない考え方だった。 | ||
|
|
||
| ChatGPTによる解説 | ||
|
|
||
| ``` | ||
| 例の途中までを見ます。 | ||
|
|
||
| [10, 11, 12, 1, 2, 3] | ||
|
|
||
| この時点では、長さ3の増加部分列が複数あります。 | ||
|
|
||
| [10, 11, 12] 長さ3、末尾12 | ||
| [1, 2, 3] 長さ3、末尾3 | ||
|
|
||
| どちらも長さは同じですが、将来伸ばしやすいのは末尾が小さい [1, 2, 3] です。 | ||
|
|
||
| 次に 4 が来た場合、 | ||
|
|
||
| [10, 11, 12] + 4 → 追加できない | ||
| [1, 2, 3] + 4 → [1, 2, 3, 4] | ||
|
|
||
| となります。 | ||
|
|
||
| 前半の再帰コードは、最初に [10, 11, 12] を選んだ時点で、後から始まる [1]、[1, 2]、[1, 2, 3] という可能性を捨てています。 | ||
|
|
||
| ## 「最長のものだけ覚える」では情報が足りない | ||
|
|
||
| コードが再帰から返している情報は、 | ||
|
|
||
| (length_of_lis, そのLISの末尾) | ||
|
|
||
| の1組だけです。 | ||
|
|
||
| しかし本当に必要なのは、たとえば次のような複数の候補です。 | ||
|
|
||
| 長さ1なら、末尾をどこまで小さくできるか | ||
| 長さ2なら、末尾をどこまで小さくできるか | ||
| 長さ3なら、末尾をどこまで小さくできるか | ||
| ... | ||
|
|
||
| [10, 11, 12, 1] を処理した時点では、 | ||
|
|
||
| 長さ1の候補: [1] 末尾1 | ||
| 長さ2の候補: [10, 11] 末尾11 | ||
| 長さ3の候補: [10, 11, 12] 末尾12 | ||
|
|
||
| という情報を残す必要があります。 | ||
|
|
||
| 長さ3の候補だけを見れば [10, 11, 12] が最長ですが、次に 2 が来たときは、長さ1の [1] が重要になります。 | ||
|
|
||
| [1] + 2 → [1, 2] | ||
|
|
||
| さらに 3, 4 が来ることで、最終的に長さ4になります。 | ||
| ``` | ||
|
|
||
| ## step2(整形&他の人のコードを読む) | ||
|
|
||
| > うーん、読みにくいと思ったんですが、理由が | ||
| > lengths | ||
| > の意味がパズルになっているからだと思いました。 | ||
| > | ||
| > 日本語で長々と書くと、「lengths[i] とは、仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ですよね。 | ||
| > | ||
| > まあ、「長さ(複数)」であることには間違いないですが、「長さ」とだけいわれて、ああ「仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ってことね、とならず、それを推測するパズルになっています。 | ||
| > | ||
| > これを前提とすると、内側のループは、関数にすることができるはずで、「i よりも左で、nums[i] 未満で終わる最大のシーケンスの長さ」を返させればいいですね。 | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1200089668901937312/1209563502407065602 | ||
|
|
||
| step1 の解説を読むと、 | ||
|
|
||
| > しかし本当に必要なのは、たとえば次のような複数の候補です。 | ||
| > 長さ1なら、末尾をどこまで小さくできるか | ||
| > 長さ2なら、末尾をどこまで小さくできるか | ||
| > 長さ3なら、末尾をどこまで小さくできるか | ||
|
|
||
| といいながら、末尾ごとに最大長さを持っている = 長さの重複があって情報が冗長、という感じがする。 | ||
|
|
||
| 素直に、「長さごとの最小末尾要素」を持つようにするとどうなるか。 | ||
| → | ||
|
|
||
| > 2番の解法は、上のリンクで書いた私の考えに近いものです。 | ||
| > | ||
| > 2番というのは、 | ||
| > 頭から舐めていって k 番目まで見たときに、 | ||
| > 「nums[k] までを使って、すべての長さ1のシーケンスを考えたときに、そのシーケンスの一番お尻の最小値」 | ||
| > 「nums[k] までを使って、すべての長さ2のシーケンスを考えたときに、そのシーケンスの一番お尻の最小値」 | ||
| > 「nums[k] までを使って、すべての長さ3のシーケンスを考えたときに、そのシーケンスの一番お尻の最小値」 | ||
| > …… | ||
| > というものです。これは昇順に並んでいるはずで、nums[k + 1] を使って、これを更新することは可能ですね。 | ||
| > | ||
| > これは、sorted array なので二分探索で、どこを更新するかは絞れます。 | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1200089668901937312/1209563502407065602 | ||
|
|
||
| ## step3(10分以内にさっとかける \* 3回) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| length_from_begin = [1 for i in range(len(nums))] | ||
| for i in range(len(nums)): | ||
| for j in range(i): | ||
| if nums[i] > nums[j]: | ||
| length_from_begin[i] = max( | ||
| length_from_begin[i], length_from_begin[j] + 1 | ||
| ) | ||
|
|
||
| return max(length_from_begin) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| from bisect import bisect_left | ||
|
|
||
|
|
||
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| smallest_end_for_lis_lentgh: list[int] = [] | ||
| for i in range(len(nums)): | ||
| length_to_update = bisect_left(smallest_end_for_lis_lentgh, nums[i]) | ||
| if length_to_update == len(smallest_end_for_lis_lentgh): | ||
| smallest_end_for_lis_lentgh.append(nums[i]) | ||
| else: | ||
| smallest_end_for_lis_lentgh[length_to_update] = nums[i] | ||
|
|
||
| return len(smallest_end_for_lis_lentgh) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| lis_length_ending_at = [1 for i in range(len(nums))] | ||
|
|
||
| def max_extendable_lis_length_before(index: int) -> int: | ||
| max_length = 0 | ||
| for i in range(index): | ||
| if nums[i] < nums[index]: | ||
| max_length = max(max_length, lis_length_ending_at[i]) | ||
|
|
||
| return max_length | ||
|
|
||
| for i in range(len(nums)): | ||
| lis_length_ending_at[i] = max_extendable_lis_length_before(i) + 1 | ||
|
|
||
| return max(lis_length_ending_at) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| from bisect import bisect_left | ||
|
|
||
|
|
||
| class Solution: | ||
| def lengthOfLIS(self, nums: list[int]) -> int: | ||
| smallest_end_for_lengths = [] | ||
| for i in range(len(nums)): | ||
| length_to_update = bisect_left(smallest_end_for_lengths, nums[i]) | ||
| if length_to_update == len(smallest_end_for_lengths): | ||
| smallest_end_for_lengths.append(nums[i]) | ||
| else: | ||
| smallest_end_for_lengths[length_to_update] = nums[i] | ||
|
|
||
| return len(smallest_end_for_lengths) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
自分なら length_to_min_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.
ありがとうございます
自分でも dict だとそのような
key_to_value型の命名をよくするので、それと同じようなものだと思えばそう名づけるのも良さそうです