Skip to content

392. Is Subsequence#52

Open
mamo3gr wants to merge 3 commits intomainfrom
392_is-subsequence
Open

392. Is Subsequence#52
mamo3gr wants to merge 3 commits intomainfrom
392_is-subsequence

Conversation

@mamo3gr
Copy link
Owner

@mamo3gr mamo3gr commented Feb 27, 2026

問題

https://leetcode.com/problems/is-subsequence/

学習方法

標準的な進め方と同様にしている。

Step 1

  1. 5分考える
  2. 分からなかったら一度答えを見る
  3. 答えを隠して空白から書いてみて、正解になったらOK

Step 2

  1. コードを自分なりに整形する
  2. 他の人のコードとコメントを読み、それを踏まえて再整形する。正解することを確認する

Step 3

「10分以内に正解するコードが書ける」ことを確認する

(その後、レビュー依頼をする=本Pull Request)

`t_i + 1` が `indices` のどこにinsertできるか?を `bisect.bisect_left` で探せばいい。
これが右端になるようなら、もう `t` には出現しないので False を返す。

これで時間計算量が `O(logN)` になった。
Copy link

Choose a reason for hiding this comment

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

Gemini の解法に違和感があったため、自分でも解いてみました。

index_and_char_to_index[t_index][ch] = t_index 以降で直近で ch が現れるインデックス + 1 というテーブルを作ってあげると、 s の中の 1 文字あたりの処理の時間計算量が O(1) となり、 s 一つあたり O(|s|) となると思います。

コーナーケースを何度か踏んでしまいましたが、こんな感じになりました。

class Solution {
public:
    static constexpr int FAILURE = -1;
    bool isSubsequence(string s, string t) {
        if (s.empty()) {
            return true;
        } 

        if (t.empty()) {
            return false;
        }

        // +1は番兵
        std::vector<vector<int>> index_and_char_to_index(t.size() + 1, std::vector<int>(128, -1));
        for (char ch = 'a'; ch <= 'z'; ++ch) {
            // 番兵を置く
            index_and_char_to_index[t.size()][ch] = FAILURE;
            int next_index = FAILURE;
            for (int i = t.size() - 1; i >= 0; --i) {
                if (t[i] == ch) {
                    next_index = i + 1;
                }
                index_and_char_to_index[i][ch] = next_index;
            }
        }

        // for (char ch = 'a'; ch <= 'z'; ++ch) {
        //     std::cout << ch;
        //     for (int i = 0; i < t.size(); ++i) {
        //         std::cout << " " << index_and_char_to_index[i][ch];
        //     }
        //     std::cout << std::endl;
        // }

        int index = 0;
        for (char ch : s) {
            int next_index = index_and_char_to_index[index][ch];
            // std::cout << "index=" << index << " ch=" << ch << " next_index=" << next_index << std::endl;
            index = next_index;
            if (index == FAILURE) {
                break;
            }
        }
        
        return index != FAILURE;
    }
};

テーブルを後ろから作るのがポイントだと思います。デバッグ中に書いたデバッグコードも、参考になれば幸いです。

@@ -0,0 +1,11 @@
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
t_i = 0
Copy link

Choose a reason for hiding this comment

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

自分ならもう少し叙述的に t_index とするのですが、 t_i でも読み手にとって十分伝わると思います。

else:
t_i += 1

reach_end_of_s = s_i == len(s)
Copy link

Choose a reason for hiding this comment

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

一度変数に置くのは冗長に感じました。 return s_i == len(s) で十分だと思います。

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.

2 participants