diff --git a/560/560. Subarray Sum Equals K.md b/560/560. Subarray Sum Equals K.md new file mode 100644 index 0000000..28ca770 --- /dev/null +++ b/560/560. Subarray Sum Equals K.md @@ -0,0 +1,132 @@ +# [560. Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/description/) +## Step1 +### 問題意図の考察 +- 問題文の確認 + - いくつか分からない語があった。 subarrays, contiguous連続? + - 整数配列'nums'と整数'k'が与えられ、合計がちょうど整数kと同じsubarraysの合計の数を返す。例を見るが例1が当てはまらないように感じて、? + - 和訳調べる + - subarrays(部分配列), contiguous(連続). ここでの部分配列とは、配列の連続した要素からなる、空ではない区間のことを指す。 + - 各例の意味がわかった。 + - ex1. nums[0]+nums[1] = 2(k), nums[1]+nums[2] = 2(k) で連続する配列がkと同じ部分配列の数を返している。 + - ex2. nums[0]+nums[1] = 3(k), nums[2] = 3(k) + +- 制約の確認 + - 1 <= nums.length <= 2 * 10^4 (20,000) + - -1000 <= nums[i] <= 1000 + - -10^7 <= k <= 10^7 (-10,000,000 ~ 10,000,000) + +- 問題意図 + - 連続する部分配列の扱い + - subarrayを見つけるのではなく、その個数を返す + - 累積和がkとなる時の数を記録する。-> 後述:部分的な思考 + +### 解法を考える。 +- 思いつかなかったので、他の人の回答を見る。 + +初回の回答 +```cpp +#include +#include + +class Solution { + public: + int subarraySum(const std::vector& nums, int k) { + std::unordered_map prefix_count; + prefix_count[0] = 1; + + int prefix_sum = 0; + int result = 0; + + for (int num : nums) { + prefix_sum += num; + auto it = prefix_count.find(prefix_sum - k); + if (it != prefix_count.end()) { + result += it->second; + } + ++prefix_count[prefix_sum]; + } + return result; + } +}; + +``` + +## Step2 +- 改善点 + - 初めの問題意図の **累積和がkとなる時の数を記録する。** が部分的、追加すべき。 + - 累積和そのものがkになったらcount, 累積和 - kが過去に何回あったかを数える。 + +- 他の方のコードを読む + - https://github.com/5ky7/arai60/pull/17/commits/592a112fcdb82cc0c76a331acb4bad27db1bf2f1 + - https://github.com/komdoroid/arai60/pull/6/commits/908a229ed1f129a2749065ce0a6eb3ff50d75cba + - odaさんの例がわかりやすかった。 + - https://discord.com/channels/1084280443945353267/1233603535862628432/1252232545056063548 + - https://discord.com/channels/1084280443945353267/1206101582861697046/1208414507735453747 + - https://github.com/Hurukawa2121/leetcode/pull/16/commits/2525739fd213a9509369768ba21c4c61d4139c07 + - キャッシュ利用などにも言及していた、まだそこまで考えれていないし、どの程度必要なのかも分からない。 + >> - 累積和を計算するのに C++17 からの `std::exclusive_scan()` が使える。 + >> - https://en.cppreference.com/w/cpp/algorithm/exclusive_scan + >> - ここで C++17 から`std::execution` のアルゴリズムの並列実行を許可する実行ポリシーがあることを知る。 + >> - https://cpprefjp.github.io/reference/execution/execution/execution_policy.html#:~:text=parallel_policy%20/%20par + >> - 確かに、排他制御をユーザが担うのかプログラムが担うのかを区別する必要がある。 + - https://github.com/5103246/LeetCode_Arai60/pull/16/commits/f3361f99bf44ff1f829d5a5320b67a1f6dbaec6b + >> いくつかの解法を書かれてて分かりやすかった。 + +- O(n^3)パターン +```cpp +#include + +class Solution { + public: + int subarraySum(const std::vector& nums, int k) { + const int n = static_cast(nums.size()); + int result = 0; + for (int begin = 0; begin < n; ++begin) { + for (int end = begin; end < n; ++end) { + int sum = 0; + for (int i = begin; i <= end; ++i) { + sum += nums[i]; + } + if (sum == k) { + ++result; + } + } + } + return result; + } +}; + + +``` + +- O(n^2)パターン +```cpp +#include + +class Solution { + public: + int subarraySum(const std::vector& nums, int k) { + const int n = static_cast(nums.size()); + int result = 0; + for (int begin = 0; begin < n; ++begin) { + int sum = 0; + for (int end = begin; end < n; ++end) { + sum += nums[end]; + if (sum == k) { + ++result; + } + } + } + return result; + } +}; + + +``` + +- 結局初回の回答で練習 + +## Step3 *自然な流れ(感覚に落とし込む) +1. 6min +2. 5min +3. 2min