-
Notifications
You must be signed in to change notification settings - Fork 0
300.Longest Increasing Subsequence #5
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: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| <問題> | ||
| https://leetcode.com/problems/longest-increasing-subsequence/description/ | ||
|
|
||
| # step1 | ||
|
|
||
| 5分程度答えを見ずに考えて、手が止まるまでやってみる。 | ||
| 何も思いつかなければ、答えを見て解く。 | ||
| ただし、コードを書くときは答えを見ないこと。 | ||
| 正解したら一旦OK。 | ||
| 思考過程もメモしてみる。 | ||
|
|
||
| ```c++ | ||
| class Solution { | ||
| public: | ||
| int lengthOfLIS(vector<int>& nums) { | ||
| vector<int> length(nums.size(), 1); | ||
| for (int i = 0; i < nums.size(); i++ ) { | ||
| for (int j = 0; j < i; j++) { | ||
| if (nums[j] < nums[i]) { | ||
| length[i] = max(length[i], length[j] + 1); | ||
| } | ||
| } | ||
| } | ||
| return *max_element(length.begin(), length.end()); | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| 【考えたこと】 | ||
| - 計算量:O(N^2) 空間:O(N) | ||
| - 漸化式にするとLength(i)=max( Length(k)+1, Length(i) ) (k<i1を満たす全てのk) | ||
| - for文でi=0から埋めていくイメージ | ||
| - 再帰でも解けるはず。step2で解く。 | ||
|
|
||
| # step2 | ||
| 他の方が描いたコードを見て、参考にしてコードを書き直してみる。 | ||
| 参考にしたコードのリンクは貼っておく。 | ||
| 読みやすいことを意識する。 | ||
| 他の解法も考えみる。 | ||
|
|
||
| 計算量:O(N) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これは自乗でしょうか。 |
||
| N:sの文字サイズ | ||
|
|
||
| ```c++ | ||
| class Solution { | ||
| private: | ||
| // rightより前の部分配列で、nums[right]よりも小さい値で終わるシーケンスの個数を返す。 | ||
| int get_maxlength_ended_at_index(int right, const vector<int>& nums, | ||
| const vector<int>& maxlengths) { | ||
| int max_length = 0; | ||
| for (int i = 0; i < right; i++) { | ||
| if (nums[i] < nums[right]) { | ||
| max_length = max(max_length, maxlengths[i]); | ||
| } | ||
| } | ||
| return max_length; | ||
| } | ||
| public: | ||
| int lengthOfLIS(vector<int>& nums) { | ||
| vector<int> maxlengths(nums.size(), 1); | ||
| for (int i = 0; i < nums.size(); i++ ) { | ||
| int max_length = get_maxlength_ended_at_index(i, nums, maxlengths); | ||
| maxlengths[i] = max_length + 1; | ||
| } | ||
| return *max_element(maxlengths.begin(), maxlengths.end()); | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| - 動的計画法については、以下動画がわかりやすかった. | ||
|
|
||
| 本問のsubproblemは、「要素iまでで連続する要素の最大長さ」 | ||
|
|
||
| https://www.youtube.com/watch?v=OQ5jsbhAv_M | ||
|
|
||
| - 命名方法・関数化 | ||
|
|
||
| > lengthsの意味がパズルになっているからだと思いました。 | ||
| > 日本語で長々と書くと、「lengths[i] とは、仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ですよね。 | ||
| > まあ、「長さ(複数)」であることには間違いないですが、「長さ」とだけいわれて、ああ「仮に nums[i] がシーケンスの最後であると仮定した場合に可能な、最も長いシーケンスの長さ」ってことね、とならず、それを推測するパズルになっています。 | ||
| > これを前提とすると、内側のループは、関数にすることができるはずで、「i よりも左で、nums[i] 未満で終わる最大のシーケンスの長さ」を返させればいいですね | ||
|
|
||
| https://github.com/hayashi-ay/leetcode/pull/27/files#diff-b7fbb0dce1473afc0264185268f1a1ef6d682a3a8c997d43bc8bdd636a66ce4aR177 | ||
|
|
||
| 上のコメントを見て、関数化した。引数が多くなっているが大丈夫?? | ||
|
|
||
| get_max_length_of_lis_before_indexは、rightより前の部分配列で、nums[right]よりも小さい値で終わるシーケンスの個数 | ||
|
|
||
| length[i]はiで終わるシーケンスの最大要素数 | ||
|
|
||
| 関数の命名方法が難しそう。一旦コメントを記載することにした。 | ||
|
|
||
|
|
||
|
|
||
| 以下、再帰でも書きました。 | ||
|
|
||
| ```c++ | ||
| class Solution { | ||
| private: | ||
| map<int, int> length; | ||
| int get_max_length_of_lis_before_index(const vector<int>& nums, int n) { | ||
| if (length.contains(n)) { | ||
| return length[n]; | ||
| } | ||
| int max_length = 1; | ||
| for (int i = 0; i < n; i++) { | ||
| if (nums[i] < nums[n]) { | ||
| max_length = max(get_max_length_of_lis_before_index(nums, i) + 1, max_length); | ||
| } | ||
| } | ||
| length[n] = max_length; | ||
| return max_length; | ||
| } | ||
| public: | ||
| int lengthOfLIS(vector<int>& nums) { | ||
| int max_length; | ||
| for (int i = 0; i < nums.size(); i++) { | ||
| max_length = get_max_length_of_lis_before_index(nums, i); | ||
| } | ||
| return max_length; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
|
|
||
| - 0(NlogN)の計算量のアルゴリズム(2分探索)もあるよう。step4で追加したい。 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://discord.com/channels/1084280443945353267/1200089668901937312/1209534429593210932 このあたりの解法、お互いに移り変われるところがあり、解法を考えるときに局所的な変形などをしています。 |
||
|
|
||
| https://github.com/sakupan102/arai60-practice/pull/32#pullrequestreview-2081534548 | ||
|
|
||
| - 類題 | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1201211204547383386/1241981541802840228 | ||
|
|
||
| - セグメントツリーでの解き方もあるが、ソフトウェアエンジニアの常識ではないとのこと。 | ||
|
|
||
|
|
||
| - 参考 | ||
|
|
||
| https://github.com/seal-azarashi/leetcode/pull/28 | ||
|
|
||
| https://github.com/TORUS0818/leetcode/pull/33/files | ||
|
|
||
| https://github.com/Ryotaro25/leetcode_first60/pull/34#discussion_r1743810106 | ||
|
|
||
|
|
||
|
|
||
|
|
||
| # step3 | ||
|
|
||
| 今度は、時間を測りながら、もう一回、書きましょう。書いてアクセプトされたら文字消してもう一回書く。これを10分以内に一回もエラーを出さずに書ける状態になるまで続ける。3回続けてそれができたらその問題はOK。 | ||
|
|
||
| 実施しました。 | ||
|
|
||
|
|
||
| ```c++ | ||
| class Solution { | ||
| private: | ||
| // rightより前の部分配列で、nums[right]よりも小さい値で終わるシーケンスの個数を返す。 | ||
| int get_maxlength_ended_at_index(int right, const vector<int>& nums, | ||
| const vector<int>& maxlengths) { | ||
| int max_length = 0; | ||
| for (int i = 0; i < right; i++) { | ||
| if (nums[i] < nums[right]) { | ||
| max_length = max(max_length, maxlengths[i]); | ||
| } | ||
| } | ||
| return max_length; | ||
| } | ||
| public: | ||
| int lengthOfLIS(vector<int>& nums) { | ||
| vector<int> maxlengths(nums.size(), 1); | ||
| for (int i = 0; i < nums.size(); i++ ) { | ||
| int max_length = get_maxlength_ended_at_index(i, nums, maxlengths); | ||
| maxlengths[i] = max_length + 1; | ||
| } | ||
| return *max_element(maxlengths.begin(), maxlengths.end()); | ||
| } | ||
| }; | ||
| ``` | ||
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.
C++ で時間計算量が O(N^2) のプログラムで N=2500 の入力データを入力して実行するとき、おおよそどれくらいの時間がかかるか推定できますか?
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.
C++の1秒間の実行ステップ数を1.010^8とすると、
計算にかかる時間=25002500/10^8〜0.0625(s)
と考えております。
以下を参考にしました。
hroc135/leetcode#9 (comment)
ちなみになのですが、C++の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.
正しいと思います。
自分の場合は大学の先輩から教えてもらったように思います。書籍やネット上の確度の高い情報で見た記憶はありません。
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.
実際のところ、いろいろな事情があるので桁くらいしか合わないものです。
CPU のインストラクションごとにクロック数が異なり、最近はマイクロコードになって並列して実行できたりできなかったりするので精度良く見積もることは困難です。
https://discord.com/channels/1084280443945353267/1218823752243089408/1244470338562293882
https://discord.com/channels/1084280443945353267/1253694251271852095/1273238168170266686
https://discord.com/channels/1084280443945353267/1200089668901937312/1235490680592273410