Skip to content
Open
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
25 changes: 25 additions & 0 deletions 15.3Sum/brute_force.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
if (nums.empty()) {
return {};
}
set<vector<int>> unique_triplets;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < nums.size(); j++) {
for (int k = 0; k < nums.size(); k++) {
if (i == j || j == k || k == i) {
continue;
}
if (nums[i] + nums[j] + nums[k] == 0) {
vector<int> triplet = {nums[i], nums[j], nums[k]};
sort(triplet.begin(), triplet.end());
unique_triplets.insert(triplet);
}
}
}
}

return vector<vector<int>>(unique_triplets.begin(), unique_triplets.end());
}
};
45 changes: 45 additions & 0 deletions 15.3Sum/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## ステップ1
Brute forceは比較的簡単に思いついた
3つの数字を追うように3つのループを使い、合計が0の場合に
vectorに入れてsortしそれをsetに入れることで重複を消す
時間計算量はO(n^3 * log n)
brute_force.cppに実装

nums[i]とnums[j]が決まれば残りの数が決まる.
残りの数をcomplimentとすると
nums[i] + nums[j] + compliment = 0
compliment = -nums[i] - nums[j]

jをループでまわす際に、setを使ってすでに通りすぎたnums[j]を記録していく
complimentがset内に入っていれば、合計して0になる組が存在するということ
これをvectorに入れてsortしそれをsetに入れることで重複を消す
時間計算量はO(n^2 * log n)
3000 = 3 * 10^3
≒ 9 * 10^6 * 3
≒ 10^7なので許容ないか
acceptまで20分

## ステップ2
変数名を微調整

最初に入力値をsortしてしまう方法もある。
https://leetcode.com/problems/3sum/editorial/
sort.cppに実装

3つの数のうちどれか一つは必ず負の数であることを利用する
ひとつ目の数字が決まったら、その次の数字と最後の数字を見ていく
合計が0になるまで前後から狭める

i == 0の場合とnums[i] != nums[i - 1]の場合に呼び出すものが同じなので
纏めるか迷うところ。比較している対象は異なる

この解法が最も素直か

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の解法


## Discordなど

44 changes: 44 additions & 0 deletions 15.3Sum/sort.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> triplets;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) {
break;
}
if (i == 0) {
FindTriplet(nums, i, triplets);
continue;
}
if (nums[i] != nums[i - 1]) {
FindTriplet(nums, i, triplets);
}
}
return triplets;
}

private:
void FindTriplet(vector<int>& nums, int index, vector<vector<int>>& triplets) {
Copy link

Choose a reason for hiding this comment

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

nums が非 const 参照だと、読み手にとって中で変更されるのだと思わせてしまうと思います。 nums は変更されないため、 const 参照で渡すことをおすすめします。

LeetCode が指定した関数宣言については、仕方ないと思います。

int left = index + 1;
int right = nums.size() - 1;
while (left < right) {
int sum = nums[index] + nums[left] + nums[right];
if (sum < 0) {
left++;
Copy link

Choose a reason for hiding this comment

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

sum < 0 と sum > 0 の場合は continue し、 sum == 0 の場合のネストを下げたほうが読みやすいと思います。

} else if (sum > 0) {
right--;
} else {
triplets.push_back({nums[index], nums[left], nums[right]});
left++;
Copy link

Choose a reason for hiding this comment

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

do while 文で回すと 1 行減ります。読みやすさはあまり変わらないかもしれません。

while (left < right && nums[left] == nums[left - 1]) {
left++;
}
right--;
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
};
32 changes: 32 additions & 0 deletions 15.3Sum/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> unique_triplets;
// 3組の内ひとつ目の数字をユニークにするために使用
set<int> first_nums;
for (int i = 0; i < nums.size(); i++) {
if (first_nums.contains(nums[i])) {
continue;
}
first_nums.insert(nums[i]);

// nums[i]に対応する(nums[j], complement)のペアを探すために
// 既にnums[j]として通りすぎた数字を記録する。
set<int> seen;
for (int j = i + 1; j < nums.size(); j++) {
int complement = -nums[i] - nums[j];
if (!seen.contains(complement)) {
seen.insert(nums[j]);
continue;
}

vector<int> triplet = {nums[i], nums[j], complement};
sort(triplet.begin(), triplet.end());
unique_triplets.insert(triplet);
}
}

// vectorに変換して返す
return vector<vector<int>>(unique_triplets.begin(), unique_triplets.end());
}
};
28 changes: 28 additions & 0 deletions 15.3Sum/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> unique_triplets;
set<int> used_first_nums;
for (int i = 0; i < nums.size(); i++) {
if (used_first_nums.contains(nums[i])) {
continue;
}
used_first_nums.insert(nums[i]);

set<int> seen;
for (int j = i + 1; j < nums.size(); j++) {
int complement = -nums[i] - nums[j];
if (!seen.contains(complement)) {
seen.insert(nums[j]);
continue;
}

vector<int> triplet = {nums[i], nums[j], complement};
sort(triplet.begin(), triplet.end());
unique_triplets.insert(triplet);
}
}

return vector<vector<int>>(unique_triplets.begin(), unique_triplets.end());
}
};
26 changes: 26 additions & 0 deletions 15.3Sum/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> unique_triplets;
set<int> used_first_nums;
for (int i = 0; i < nums.size(); i++) {
if (used_first_nums.contains(nums[i])) {
continue;
}
used_first_nums.insert(nums[i]);

set<int> seen;
for (int j = i + 1; j < nums.size(); j++) {
int complement = -nums[i] - nums[j];
if (!seen.contains(complement)) {
seen.insert(nums[j]);
continue;
}
vector<int> triplet = {nums[i], nums[j], complement};
sort(triplet.begin(), triplet.end());
unique_triplets.insert(triplet);
}
}
return vector<vector<int>>(unique_triplets.begin(), unique_triplets.end());
}
};
17 changes: 17 additions & 0 deletions 152.MaximumProductSubarray/brute_force.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int max_product = numeric_limits<int>::min();
for (int i = 0; i < nums.size(); i++) {
int temp = 1;
for (int j = i; j < nums.size(); j++) {
temp *= nums[j];
max_product = max(max_product, temp);
}
}
return max_product;
}
};
36 changes: 36 additions & 0 deletions 152.MaximumProductSubarray/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## ステップ1
まず思いついた解法はbrute forceで愚直に積を求める方法
これだと時間計算量は、O(n^2)となる
nums.lengthは2 * 10 ^ 4なので4 * 10 ^ 8 C++だとギリギリ1秒ないに処理できそう

各ステップで最大になるタイミングを追いかける
・あるnums[i]でリセットする場合(負の数を奇数個含む場合はnums[i]の方が大きくなることがある)
・正の数ばかりがsubarrayに含まれる場合
・負の数偶数個含む場合
単に正の数だけ追うのではなく負の数も追いかけると1ループで見つけられそう
時間計算量はO(n)
空間計算量はO(1)

acceptまで12分

配列が空の場合にreturn 0とする。
本当はありえない数字を返したかった。エラーメッセージを表示するなどでも良さそう。

## ステップ2
step1の変数名が長いので、変更

maxとminの使い方変更
initializer listsを使うことで複数の値でも使うことができる
https://cplusplus.com/reference/algorithm/max/

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の解法
負の数が偶数個あると最大になりうる特性を活かして途中でswapすることで
自分のstep1で行った3パターンから2パターンへ比較回数を減らすことができる(step2_2で実装)
これは初見で思いつくことが難しそうなので愚直に比較したい
https://leetcode.com/problems/maximum-product-subarray/solutions/48230/possibly-simplest-solution-with-o-n-time-complexity/

## Discorなど

19 changes: 19 additions & 0 deletions 152.MaximumProductSubarray/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int max_product = nums[0];
int max_product_so_far = nums[0];
int min_product_so_far = nums[0];
for (int i = 1; i < nums.size(); i++) {
int temp_max_so_far = max(nums[i], max(max_product_so_far * nums[i], min_product_so_far * nums[i]));
min_product_so_far = min(nums[i], min(max_product_so_far * nums[i], min_product_so_far * nums[i]));
max_product_so_far = temp_max_so_far;
max_product = max(max_product, max_product_so_far);
}

return max_product;
}
};
21 changes: 21 additions & 0 deletions 152.MaximumProductSubarray/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int max_product = nums[0];
int current_max = nums[0];
int current_min = nums[0];
for (int i = 1; i < nums.size(); i++) {
// current_maxの数字は次の行で使うので一旦退避
int temp = max({nums[i], current_max * nums[i], current_min * nums[i]});
current_min = min({nums[i], current_max * nums[i], current_min * nums[i]});

current_max = temp;
max_product = max(max_product, current_max);
}

return max_product;
}
};
21 changes: 21 additions & 0 deletions 152.MaximumProductSubarray/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int max_product = nums[0];
int max_product_so_far = nums[0];
int min_product_so_far = nums[0];
for (int i = 1; i < nums.size(); i++) {
if (nums[i] < 0) {
swap(max_product_so_far, min_product_so_far);
}
max_product_so_far = max(nums[i], max_product_so_far * nums[i]);
min_product_so_far = min(nums[i], min_product_so_far * nums[i]);
max_product = max(max_product, max_product_so_far);
}

return max_product;
}
};
21 changes: 21 additions & 0 deletions 152.MaximumProductSubarray/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.empty()) {
return 0;
}

int max_product = nums[0];
int current_max = nums[0];
int current_min = nums[0];
for (int i = 1; i < nums.size(); i++) {
int temp = max({nums[i], current_max * nums[i], current_min * nums[i]});
current_min = min({nums[i], current_max * nums[i], current_min * nums[i]});

current_max = temp;
max_product = max(max_product, current_max);
}

return max_product;
}
};