置頂

我的 VPython 教學文件 (HackMD 版本)

VPython 教學文件目錄 安裝及測試 基本語法 等速度直線運動 自由落下 終端速度 水平抛射 使用For迴圈計算水平抛射資料 斜向抛射 圓周運動 簡諧運動 單擺 木塊彈簧系統分離 重力及簡諧 行星運動 相疊木塊 雙重簡諧運動 一維彈性碰撞 ...

熱門文章

2026年10月3日 星期六

LeetCode 解題筆記:792. Number of Matching Subsequences

作者:王一哲
日期:2026年10月3日


LeetCode 題目連結:792. Number of Matching Subsequences

解題想法


中等難度題。題目給一個字串 $s$ 及一個字串陣列 $words$,要計算 $words$ 之中有幾個是 $s$ 的子序列 (subsecquence)。子序列是由 $s$ 刪除任意數量的字母,並且保持剩下的字母順序。由於這題的 $s$ 長度可達 $50000$,如果用雙層迴圈跑出 $s$ 所有的子序列會超時。我的作法是先用一個字典或陣列 $pos$,記錄 $s$ 之中每個字母的索引值,索引值由小到大排序。再寫一個檢測輸入的字串 $target$ 是否為 $s$ 子序列的函式 $check$,於函式中依序讀取 $target$ 的字母 $c$,用二分搜尋法從 $pos$ 對應到 $c$ 的資料中,搜尋是否有在前一個字母索引值 $pre$ 之後的字母 $c$,如果沒有就回傳 False;如果 $target$ 所有的字母都能被找到,回傳 True。

Python 程式碼


Runtime: 352 ms, beats 33.32%. Memory: 23.02 MB, beats 33.91%.
from bisect import bisect_left

class Solution:
    def numMatchingSubseq(self, s: str, words: list[str]) -> int:
        n = len(s)  # 長度
        # 各字母於 s 的索引值,用於二分搜尋法
        pos = [[] for _ in range(26)]
        for i in range(n):
            pos[ord(s[i]) - ord('a')].append(i)
        
        # 檢測 target 是否為 s 子序列的函式
        def check(target):
            pre = -1  # 前一個字母於 s 的索引值
            for c in target:
                arr = pos[ord(c) - ord('a')]  # 字母 c 於 s 的索引值
                j = bisect_left(arr, pre + 1)  # 找出大於等於 pre + 1 的索引值
                if j == len(arr):  # 沒找到,回傳 False
                    return False
                pre = arr[j]  # 更新 pre
            return True  # 都有找到,回傳 True
        
        # 檢測 words 之中的字串,計算答案
        ans = 0
        for word in words:
            if check(word):
                ans += 1
                
        return ans


Runtime: 282 ms, beats 57.13%. Memory: 23.22 MB, beats 16.90%.
from bisect import bisect_left

class Solution:
    def numMatchingSubseq(self, s: str, words: list[str]) -> int:
        n = len(s)  # 長度
        # 各字母於 s 的索引值,用於二分搜尋法
        pos = defaultdict(list)
        for i in range(n):
            pos[s[i]].append(i)
        
        # 檢測 target 是否為 s 子序列的函式
        def check(target):
            pre = -1  # 前一個字母於 s 的索引值
            for c in target:
                arr = pos[c]  # 字母 c 於 s 的索引值
                j = bisect_left(arr, pre + 1)  # 找出大於等於 pre + 1 的索引值
                if j == len(arr):  # 沒找到,回傳 False
                    return False
                pre = arr[j]  # 更新 pre
            return True  # 都有找到,回傳 True
        
        # 檢測 words 之中的字串,計算答案
        ans = 0
        for word in words:
            if check(word):
                ans += 1
                
        return ans


C++ 程式碼


Runtime: 62 ms, beats 77.80%. Memory: 59.39 MB, beats 34.29%.
class Solution {
private:
    vector<vector<int>> pos;  // 各字母於 s 的索引值,用於二分搜尋法
    
public:
    bool check(const string& target) {
        // 檢測 target 是否為 s 子序列的函式
        int pre = -1;  // 前一個字母於 s 的索引值
        for(char c : target) {
            int idx = c - 'a';
            auto it = lower_bound(pos[idx].begin(), pos[idx].end(), pre + 1);
            if (it == pos[idx].end()) {  // 沒找到,回傳 False
                return false;
            }
            pre = *it;  // 更新 pre
        }
        return true;  // 都有找到,回傳 True
    }
    
    int numMatchingSubseq(string s, vector<string>& words) {
        int n = (int)s.size();
        pos.assign(26, vector<int> (0));
        for(int i = 0; i < n; i++) {
            pos[s[i] - 'a'].push_back(i);
        }
        
        // 檢測 words 之中的字串,計算答案
        int ans = 0;
        for(auto word : words) {
            if (check(word)) {
                ans++;
            }
        }
        return ans;
    }
};


Runtime: 96 ms, beats 42.15%. Memory: 59.54 MB, beats 31.42%.
class Solution {
private:
    unordered_map<char, vector<int>> pos;  // 各字母於 s 的索引值,用於二分搜尋法
    
public:
    bool check(const string& target) {
        // 檢測 target 是否為 s 子序列的函式
        int pre = -1;  // 前一個字母於 s 的索引值
        for(char c : target) {
            auto it = lower_bound(pos[c].begin(), pos[c].end(), pre + 1);
            if (it == pos[c].end()) {  // 沒找到,回傳 False
                return false;
            }
            pre = *it;  // 更新 pre
        }
        return true;  // 都有找到,回傳 True
    }
    
    int numMatchingSubseq(string s, vector<string>& words) {
        int n = (int)s.size();
        pos.clear();
        for(int i = 0; i < n; i++) {
            pos[s[i]].push_back(i);
        }
        
        // 檢測 words 之中的字串,計算答案
        int ans = 0;
        for(auto word : words) {
            if (check(word)) {
                ans++;
            }
        }
        return ans;
    }
};


沒有留言:

張貼留言