Skip to content
Merged
Show file tree
Hide file tree
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
18 changes: 18 additions & 0 deletions 300.LongestIncreasingSubsequence/binary_search.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
// least increasing order
vector<int> lis;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

https://github.com/Yoshiki-Iwasa/Arai60/pull/46/files/56e8cf4d4efc42c5784108191d1e5fc615de9206#r1716128766
他のPRでたしかに、と思う議論があったので貼っておきます。厳密にはこれはLISではないという話です

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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

たとえばnums = [10, 11, 12, 1, 2]を与えると、lisは[1, 2, 12]になると思います。実際には[1, 2, 12]という部分列を取ることはできません。
日本語でいうと、「長さ index の IS の末尾の最小値」なので、end_minimums_of_is とかですかね。難しいですね。

この辺りですね。確かに中身を追っていくと一致しないので命名難しいです。


for (auto num : nums) {
auto it = lower_bound(lis.begin(), lis.end(), num);
if (it == lis.end()) {
lis.push_back(num);
} else {
*it = num;
}
}

return lis.size();
}
};
24 changes: 24 additions & 0 deletions 300.LongestIncreasingSubsequence/iteration_step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
vector<int> increasing_nums;
increasing_nums.push_back(nums[0]);
for (int i = 1; i < nums.size(); i++) {
if (nums[i] > increasing_nums.back()) {
increasing_nums.push_back(nums[i]);
continue;
}
// find first larger or equal num
int greater_or_equal_index = 0;
while (nums[i] > increasing_nums[greater_or_equal_index]) {
greater_or_equal_index++;
}
increasing_nums[greater_or_equal_index] = nums[i];
}

return increasing_nums.size();
}
};
43 changes: 43 additions & 0 deletions 300.LongestIncreasingSubsequence/iteration_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
vector<int> increasing_nums;
increasing_nums.push_back(nums[0]);
for (int i = 1; i < nums.size(); i++) {
if (nums[i] > increasing_nums.back()) {
increasing_nums.push_back(nums[i]);
continue;
}
// find first larger or equal num
int index = FindFirstLargerOrEqualIndex(increasing_nums, nums[i]);
increasing_nums[index] = nums[i];
}

return increasing_nums.size();
}

private:
int FindFirstLargerOrEqualIndex(vector<int>& nums, int target) {
int left = 0;
int right = nums.size();

while (left < right) {
int middle = left + (right - left) / 2;
if (nums[middle] < target) {
// middle の値がターゲットより小さい場合、
// ターゲットは middle の右側にある可能性がある。
// したがって、探索範囲を [middle + 1, right) に絞る
left = middle + 1;
} else {
// middle の値がターゲット以上の場合、
// middle は答えの候補であるか、ターゲットは middle の左側にある可能性がある。
// したがって、探索範囲を [left, middle) に絞る。
right = middle;
}
}
return left;
}
};
52 changes: 52 additions & 0 deletions 300.LongestIncreasingSubsequence/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## ステップ1
思いついたのは愚直にループを回しながら頭からみていく方法と
頭から1歩ずつ進んでいき、地点ごとの最大距離をメモ化する

メモ化のロジックを、形で覚えてしまっている感がある
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この解法はDPのテーブルを埋めているだけでメモ化ではないような気がします。

https://en.wikipedia.org/wiki/Memoization

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls to pure functions and returning the cached result when the same inputs occur again.
(強調引用者)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@fhiyo

longest_lengths[j] + 1の部分が、過去に入力されたデータを使っているイメージだったのですが再帰呼び出しの関数の結果を保存していることなのですね。誤って認識しておりました🙇

15分ほどでaccept

時間計算量
O(n^2)

空間計算量
O(n)

## ステップ2
・メモ化のvectorの名前をmemoizationから変更

## 他の解法
binary_searchでも解くことができる
https://github.com/Yoshiki-Iwasa/Arai60/pull/46/commits/56e8cf4d4efc42c5784108191d1e5fc615de9206

こちらも二分探索
他にもBitとセグメント木というものがある(名前を聞いたことがあるようなないような。。。)
ぱっと見「アルゴリズムイントロダクション」に載っていない?
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ソフトウェアエンジニアの常識から微妙に外れていると思います。知っている人は多いけれども、知らなくても別に動揺されないものです。
つまり、これを使ってできる高速化はできなくても構わないのですが、しかし、考える時にこれも知っていると、セグメントツリーを使わない方法を思いついたりするので、知っていてもいいかもしれません。

https://github.com/fhiyo/leetcode/pull/32/commits/4ddb934198ff85e6349b064edea5fe312bd27b9c

基本方針はみなさんメモ化かな、二分探索もできた方がいいか
https://github.com/SuperHotDogCat/coding-interview/pull/28/commits/b81e3d1929c84abea2f00ef3b20b5a34b79eec38
https://github.com/sakupan102/arai60-practice/pull/32

マジックナンバーについて
> 入力の制約を守らない入力を入れると、理解できない振る舞いをするんですよね。
> 数年間使うコードならば、そういうことは間違いなく起きますよね。
制約値よりは、std::numeric_limits::min()とstd::numeric_limits::max()を使うか
https://github.com/Exzrgs/LeetCode/pull/18

## 二分探索
binary_search.cppに実装
numsの中の最小値に出会うたびに先頭から入れ替えが走る
入れ替え後から大きな数が現れると要素が後ろに追加される

https://en.cppreference.com/w/cpp/algorithm/lower_bound
時間計算量
O(n log n)
要素数がnでそのループ内のlower_boundがlog n

空間計算量
O(n)

メモ:下記のケースを使うと理解しやすい
{10, 9, 2, 5, 3, 7, 101, 18, 1, 2, 3, 4, 5, 6, 7, 8}


18 changes: 18 additions & 0 deletions 300.LongestIncreasingSubsequence/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> memoization(nums.size(), 1);

int max_length = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

iとjの位置関係が分かりにくく感じました。 nums[j] < nums[i] の書き方の方が j < i であることが分かりやすい気がします。

memoization[i] = max(memoization[i], memoization[j] + 1);
}
}
max_length = max(max_length, memoization[i]);
}

return max_length;
}
};
18 changes: 18 additions & 0 deletions 300.LongestIncreasingSubsequence/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> longest_lengths(nums.size(), 1);

int max_length = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
longest_lengths[i] = max(longest_lengths[i], longest_lengths[j] + 1);
}
}
max_length = max(max_length, longest_lengths[i]);
}

return max_length;
}
};
19 changes: 19 additions & 0 deletions 300.LongestIncreasingSubsequence/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> longest_lengths(nums.size(), 1);

int max_length = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
longest_lengths[i] = max(longest_lengths[i], longest_lengths[j] + 1);
}
}

max_length = max(max_length, longest_lengths[i]);
}

return max_length;
}
};
19 changes: 19 additions & 0 deletions 300.LongestIncreasingSubsequence/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> longest_lengths(nums.size(), 1);

int max_length = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
longest_lengths[i] = max(longest_lengths[i], longest_lengths[j] + 1);
}
}

max_length = max(max_length, longest_lengths[i]);
}

return max_length;
}
};
24 changes: 24 additions & 0 deletions problem_name/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
vector<int> increasing_nums;
increasing_nums.push_back(nums[0]);
for (int i = 1; i < nums.size(); i++) {
if (nums[i] > increasing_nums.back()) {
increasing_nums.push_back(nums[i]);
continue;
}
// find first larger or equal num
int greater_or_equal_index = 0;
while (nums[i] > increasing_nums[greater_or_equal_index]) {
greater_or_equal_index++;
}
increasing_nums[greater_or_equal_index] = nums[i];
}

return increasing_nums.size();
}
};