diff --git a/392.IsSubsequence/divide.cpp b/392.IsSubsequence/divide.cpp new file mode 100644 index 0000000..85c2f53 --- /dev/null +++ b/392.IsSubsequence/divide.cpp @@ -0,0 +1,25 @@ +class Solution { + public: + bool isSubsequence(string s, string t) { + // 整数リテラルは非constなint&にはバインドできないので変数を用意する + int source_index = 0; + int target_index = 0; + return CheckIsSubsequence(source_index, s, target_index, t); + } + + private: + bool CheckIsSubsequence(int& source_index, string& source, + int& target_index, string& target) { + if (source_index == source.size()) { + return true; + } + if (target_index == target.size()) { + return false; + } + if (source[source_index] == target[target_index]) { + source_index++; + } + target_index++; + return CheckIsSubsequence(source_index, source, target_index, target); + } + }; diff --git a/392.IsSubsequence/divide_step2.cpp b/392.IsSubsequence/divide_step2.cpp new file mode 100644 index 0000000..86dfdaa --- /dev/null +++ b/392.IsSubsequence/divide_step2.cpp @@ -0,0 +1,25 @@ +class Solution { + public: + bool isSubsequence(string s, string t) { + // 整数リテラルは非constなint&にはバインドできないので変数を用意する + int source_index = 0; + int target_index = 0; + return CheckIsSubsequence(source_index, s, target_index, t); + } + + private: + bool CheckIsSubsequence(int source_index, string& source, + int target_index, string& target) { + if (source_index == source.size()) { + return true; + } + if (target_index == target.size()) { + return false; + } + if (source[source_index] == target[target_index]) { + source_index++; + } + target_index++; + return CheckIsSubsequence(source_index, source, target_index, target); + } + }; diff --git a/392.IsSubsequence/map.cpp b/392.IsSubsequence/map.cpp new file mode 100644 index 0000000..5661d10 --- /dev/null +++ b/392.IsSubsequence/map.cpp @@ -0,0 +1,32 @@ +class Solution { + public: + bool isSubsequence(string s, string t) { + map> letter_to_indices; + for (int i = 0; i < t.size(); i++) { + letter_to_indices[t[i]].push_back(i); + } + + // 探索前なのでありえない値で初期化 + int current_index = -1; + for (char letter : s) { + if (!letter_to_indices.contains(letter)) { + return false; + } + + bool is_matched = false; + for (int matched_index : letter_to_indices[letter]) { + if (current_index > matched_index) { + continue; + } + current_index = matched_index; + is_matched = true; + break; + } + // 順番を考慮してマッチしない場合 + if (!is_matched) { + return false; + } + } + return true; + } + }; diff --git a/392.IsSubsequence/memo.md b/392.IsSubsequence/memo.md new file mode 100644 index 0000000..1fe5882 --- /dev/null +++ b/392.IsSubsequence/memo.md @@ -0,0 +1,82 @@ +## ステップ1 +sが空の場合は、t内の文字を全て削除すれば作れるのでtrueですよね? + +思いついた方法は愚直にsをループで回し、tが含まれるのかチェック。 +tのインデックスを記憶しておき、sの次の文字の探索開始位置をそこにする。 +sの長さをm +tの長さをn +これだとO(m n) 10^4 * 100 なので10^6か + +queueを使えば、二重ループにしなくても解けそう。 +>without disturbing the relative positions of the remaining characters. +問題文に丁寧にこう書いてあるので、queueが向いてる。 +sの文字を順番に突っ込む。tを頭から探索して一致する文字があればqueueから取り出す。 +最終的にqueueが空かどうかでとく。 +tの長さに釣られて +時間計算量O(n) +空間計算量O(n) + +## ステップ2 +step1に対して +queueに突っ込んだがforループで見ていく場合は前から見ていくので +queueにつっこむ意味はない +sの文字を見ていくのではなく、indexを見ていく +tを頭から見ていき、s[index]が現れた場合のみsのindexを更新する + +leetcodeの解答より +バイナリーサーチ、グリーディー、ダイナミックプログラミングで解くことができる。 +=>どの回答もstep1の選択肢を考える際に思いつかなかった。 + +・divide.cppを実装 + 一文字ずつに分解して、sがtに含まれるのか再帰的に探索する + 文字をのまま渡すのではなく添字で管理する。 + + sとtの先頭文字が一致すれば、sの添字を進める。そうでなければスキップする。 + 再帰呼び出しのたびにtの添字は進める。 + 時間はO(n)で、空間はO(n)で解くことができる。 + +・two_pointers.cpp + divide.cppだと再帰呼び出しのたびにスタックメモリを使用する(関数の戻り先か)ので再帰を使わずループに変更。 + =>この解法は初見で思いつきたかった。 + ループのたびにtを探索する添字はインクリメントする。 + sを探索する添字は探索中の文字が一致した場合のみとする。 + 最終的にsの添字がsの長さと一致していれば、Subsequenceを作ることができるということ。 + 時間はO(n)で、空間はO(1)で解くことができる。 + +・map.cpp + s側ではなくt側をmapに突っ込み文字に対する出現位置を全て入れる + sを頭から一文字ずつ確認する。 + 登場位置の制約を守りながら、全ての文字が登場するかどうかを確認することで解ける。 + この方法であれば毎回t全体を探索することを避けることができる。 + +## ステップ3 +**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。** + +## 他の方の解法 +>std::vector については何度か議論があったと思います。それらを意識したうえで使うのであれば大丈夫だと思います。過去のコメントを探してみることをお勧めいたします。 +今回は使っていないが、vectorの特殊化については認識しておく +https://cpprefjp.github.io/reference/vector/vector.html + +>setを使う場合はcharacterPositionsの初期化がO(N)ではなくO(NlogN)になりそうかなと思ったのですが、 +insertする際の計算量見落としがちなので意識する。 +・size_tを使うかintを使いかは文脈次第 + 過去の問題で受けたsize_tは, unsigned intであるということを意識して使う + +>競合状態を避けるため、副作用のある書き方は現実的な範囲でできるだけ避けたほうがよいと思います。 +leetcodeだけの話だと見落としてしまいち +https://github.com/usatie/leetcode/pull/5/files/db0790afcbe0e57e141f025f902c1226890976b6#diff-96848deece2c6608d931aa2cb2aaf0ffd78c9fc4cd7337d89293dba090c204af + + +>s, t はどっちがどっちか分からないので避けたいですね。 +書き換えられる範囲で意識しよう +https://github.com/philip82148/leetcode-arai60/pull/7 + +選択肢としてはhashmap、dp、two pointersか +https://github.com/Yoshiki-Iwasa/Arai60/pull/62/commits/09cc9ca0456c7df9c0f875c387207bc61727c787 + +辞書と二分探索を混ぜた解法 +一見単純な問題でも選択肢たくさんあるんですね。 +https://github.com/fhiyo/leetcode/pull/55/commits/3bdbb38c39f00eba0213b5fb82d1cddd75e55537 + +## Discorなど + diff --git a/392.IsSubsequence/step1.cpp b/392.IsSubsequence/step1.cpp new file mode 100644 index 0000000..8116dea --- /dev/null +++ b/392.IsSubsequence/step1.cpp @@ -0,0 +1,17 @@ +class Solution { + public: + bool isSubsequence(string s, string t) { + queue letters_to_subsequence; + for (auto& letter : s) { + letters_to_subsequence.push(letter); + } + + for (auto& letter : t) { + if (letter == letters_to_subsequence.front()) { + letters_to_subsequence.pop(); + } + } + + return letters_to_subsequence.empty(); + } + }; diff --git a/392.IsSubsequence/step2.cpp b/392.IsSubsequence/step2.cpp new file mode 100644 index 0000000..227ecd7 --- /dev/null +++ b/392.IsSubsequence/step2.cpp @@ -0,0 +1,14 @@ +class Solution { + public: + bool isSubsequence(string subsequence, string text) { + int subsequence_index = 0; + + for (int i = 0; i < text.size(); i++) { + if (subsequence[subsequence_index] == text[i]) { + subsequence_index++; + } + } + + return subsequence_index == subsequence.size(); + } + }; diff --git a/392.IsSubsequence/step3.cpp b/392.IsSubsequence/step3.cpp new file mode 100644 index 0000000..1abf050 --- /dev/null +++ b/392.IsSubsequence/step3.cpp @@ -0,0 +1,13 @@ +class Solution { + public: + bool isSubsequence(string subsequence, string text) { + int subsequence_index = 0; + for (int i = 0; i < text.size(); i++) { + if (subsequence[subsequence_index] == text[i]) { + subsequence_index++; + } + } + + return subsequence_index == subsequence.size(); + } + }; diff --git a/392.IsSubsequence/step4.cpp b/392.IsSubsequence/step4.cpp new file mode 100644 index 0000000..7120203 --- /dev/null +++ b/392.IsSubsequence/step4.cpp @@ -0,0 +1,31 @@ +// map.cppからレビューを反映したもの +class Solution { + public: + bool isSubsequence(string s, string t) { + // ASCII文字列として格納する + vectorletters_to_indices[128]; + for (int i = 0; i < t.size(); i++) { + letters_to_indices[static_cast(t[i])].push_back(i); + } + + // 探索前なのでありえない値で初期化 + int current_index = -1; + for (char letter : s) { + int letter_index = static_cast(letter); + if (letters_to_indices[letter_index].empty()) { + return false; + } + + auto it = lower_bound(letters_to_indices[letter_index].begin(), + letters_to_indices[letter_index].end(), + current_index + 1); + if (it == letters_to_indices[letter_index].end()) { + return false; + } + + // 順番を考慮してマッチしない場合 + current_index = *it; + } + return true; + } + }; diff --git a/392.IsSubsequence/step5.cpp b/392.IsSubsequence/step5.cpp new file mode 100644 index 0000000..3e6dfe7 --- /dev/null +++ b/392.IsSubsequence/step5.cpp @@ -0,0 +1,16 @@ +class Solution { + public: + bool isSubsequence(string subsequence, string text) { + int subsequence_index = 0; + for (int i = 0; i < text.size(); i++) { + if (subsequence_index == subsequence.size()) { + break; + } + if (subsequence[subsequence_index] == text[i]) { + subsequence_index++; + } + } + + return subsequence_index == subsequence.size(); + } + }; diff --git a/392.IsSubsequence/two_pointers.cpp b/392.IsSubsequence/two_pointers.cpp new file mode 100644 index 0000000..f57a433 --- /dev/null +++ b/392.IsSubsequence/two_pointers.cpp @@ -0,0 +1,15 @@ +class Solution { + public: + bool isSubsequence(string s, string t) { + int source_index = 0; + int target_index = 0; + while (source_index < s.size() && target_index < t.size()) { + if (s[source_index] == t[target_index]) { + source_index++; + } + target_index++; + } + + return source_index == s.size(); + } + };