Skip to content

Conversation

@Ryotaro25
Copy link
Owner

問題へのリンク
https://leetcode.com/problems/is-subsequence/description/

問題文(プレミアムの場合)

備考

次に解く問題の予告
Next Permutation

フォルダ構成
LeetCodeの問題ごとにフォルダを作成します。
フォルダ内は、step1.cpp、step2.cpp、step3.cpp、divide.cpp、 map.cpp、two_pointers.cppとmemo.mdとなります。

memo.md内に各ステップで感じたことを追記します。

class Solution {
public:
bool isSubsequence(string s, string t) {
// 整数リテラルは非constなint&にはバインドできないので変数を用意する
Copy link

Choose a reason for hiding this comment

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

CheckIsSubsequence() を呼び出したあとに source_index、 target_index の値を利用しないため、参照渡しにする必然性がないように感じました。自分なら値渡しで書くと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip レビューありがとうございます。確かに不要でした。
『Effective C++ 第3版』の21項に書かれていた通り根絶やしにしようとしておりました。

『Effective C++ 第3版』の20項に組み込み型に関しては普通値渡しと説明ございました。
55000b0

s側ではなくt側をmapに突っ込み文字に対する出現位置を全て入れる
sを頭から一文字ずつ確認する。
登場位置の制約を守りながら、全ての文字が登場するかどうかを確認することで解ける。
この方法であれば毎回t全体を探索することを避けることができる。
Copy link

Choose a reason for hiding this comment

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

s が 'a' 100 文字、 t が 'a' 10000 文字の場合、 letter_to_indices['a'] を毎回走査する必要があります。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip
このケース見えておりませんでした🙇‍♂️ 他の箇所でレビュー頂いたbinary searchとvector letter_to_indices[128]を使う方式を追加しました。step4.cppです。

変更前
100 * 10000 = 10^5
変更後
100 * log_2(10000) = 100 * 13.28 = 1300

アクセスパターンがランダムアクセスになるため、データサイズによっては逆に遅くなるかもしれません。
この場合も認識しておきます🙇‍♂️

class Solution {
public:
bool isSubsequence(string s, string t) {
map<char, vector<int>> letter_to_indices;
Copy link

Choose a reason for hiding this comment

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

vector<int> letter_to_indices[128]; とすると、 map を省けるので、少し軽くなる可能性があります。


bool is_matched = false;
for (int matched_index : letter_to_indices[letter]) {
if (current_index > matched_index) {
Copy link

Choose a reason for hiding this comment

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

std::lower_bound() でインデックスを探すと、時間計算量的には速くなると思います。ただ、アクセスパターンがランダムアクセスになるため、データサイズによっては逆に遅くなるかもしれません。

public:
bool isSubsequence(string s, string t) {
queue<char> letters_to_subsequence;
for (auto& letter : s) {
Copy link

Choose a reason for hiding this comment

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

参照で受け取った場合、出力されるコードがアドレス経由で要素にアクセスするものになる可能性があり、処理が重くなる可能性があります。コンパイラーによる最適化が働けば問題ないのですが、期待しすぎるのもよくないと思います。 CPU のレジスターのビット幅に比べて小さい要素については、値で受け取ったほうがよいと思います。詳しくは『Effective C++ 第3版』をご覧ください。

Copy link
Owner Author

Choose a reason for hiding this comment

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

@nodchip
C++のコンパイラや実行環境によりサイズが大きくなることと組み込み型は普通値渡しと説明ございました🙇‍♂️
何度か読み返すようにします。

int subsequence_index = 0;

for (int i = 0; i < text.size(); i++) {
if (subsequence[subsequence_index] == text[i]) {
Copy link

Choose a reason for hiding this comment

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

subsequence で配列外アクセスをしているように見えたのですが、規格上は問題ないのですね。

https://timsong-cpp.github.io/cppwp/n4950/string.access

Preconditions: pos <= size().
Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object to any value other than charT() leads to undefined behavior.

読む人にとって紛らわしいため、自分なら subsequence_index == size() となるときにアクセスしないようなコードを書くと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ここも意識できておりませんでした。t側が大きい場合に、確かにエラ-が出そうですね。


for (int i = 0; i < text.size(); i++) {
if (subsequence[subsequence_index] == text[i]) {
subsequence_index++;
Copy link

Choose a reason for hiding this comment

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

subsequence_index == subsequence.size() の時に early return してもよいと思います。

source_index++;
}
target_index++;
return CheckIsSubsequence(source_index, source, target_index, target);
Copy link

Choose a reason for hiding this comment

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

この形の末尾再帰は容易にループに直せます。全体を while (1) {} でくくって、引数を代入に変えればいいのです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants