[LeetCode] Number of Matching Subsequences 匹配的子序列的個數


 

Given string S and a dictionary of words words, find the number of words[i] that is a subsequence of S.

Example :
Input: 
S = "abcde"
words = ["a", "bb", "acd", "ace"]
Output: 3
Explanation: There are three words in words that are a subsequence of S: "a", "acd", "ace".

Note:

  • All words in words and S will only consists of lowercase letters.
  • The length of S will be in the range of [1, 50000].
  • The length of words will be in the range of [1, 5000].
  • The length of words[i] will be in the range of [1, 50].

 

這道題給了我們一個字符串S,又給了一個單詞數組,問我們數組有多少個單詞是字符串S的子序列。注意這里是子序列,而不是子串,子序列並不需要連續。那么只要我們知道如何驗證一個子序列的方法,那么就可以先嘗試暴力搜索法,就是對數組中的每個單詞都驗證一下是否是字符串S的子序列。驗證子序列的方法就是用兩個指針,對於子序列的每個一個字符,都需要在母字符中找到相同的,在母字符串所有字符串遍歷完之后或之前,只要子序列中的每個字符都在母字符串中按順序找到了,那么就驗證成功了。很不幸,這種暴力搜索的方法在C++的解法版本中會TLE,貌似Java版本的可以通過,感覺C++被dis了誒~ However,我們可以進行優化呀,在暴力搜索的基礎上稍作些優化,就可以騙過OJ啦。下面這種優化的motivation是由於看了使暴力搜索跪了的那個test case,其實是words數組里有大量相同的單詞,而且字符串S巨長無比,那么為了避免相同的單詞被不停的重復檢驗,我們用兩個HashSet來記錄驗證過的單詞,為啥要用兩個呢?因為驗證的結果有兩種,要么通過,要么失敗,我們要分別存在兩個HashSet中,這樣再遇到每種情況的單詞時,我們就知道要不要結果增1了。如果單詞沒有驗證過的話,那么我們就用雙指針的方法進行驗證,然后根據結果的不同,存到相應的HashSet中去,參見代碼如下:

 

解法一:

class Solution {
public:
    int numMatchingSubseq(string S, vector<string>& words) {
        int res = 0, n = S.size();
        unordered_set<string> pass, out;
        for (string word : words) {
            if (pass.count(word) || out.count(word)) {
                if (pass.count(word)) ++res; 
                continue;
            }
            int i = 0, j = 0, m = word.size();
            while (i < n && j < m) {
                if (word[j] == S[i]) ++j;
                ++i;
            }
            if (j == m) {++res; pass.insert(word);}
            else out.insert(word);
        }
        return res;
    }
};

 

上面的解法已經優化的不錯了,但是我們還有更叼的方法。這種解法按照每個單詞的首字符進行群組,群組里面保存的是一個pair對,由當前字母和下一個位置組成的。然后在遍歷字符串S的時候,根據當前遍歷到的字母,進入該字母對應的群組中處理,如果群組中某個pair的下一個位置已經等於單詞長度了,說明該單詞已經驗證完成,是子序列,結果自增1;否則的話就將下一個位置的字母提取出來,然后將pair中的下一個位置自增1后組成的新pair加入之前提取出的字母對應的群組中。是不是讀到這里已然懵逼了,沒關系,博主會舉栗子來說明的,就拿題目中的那個例子來說吧:

S = "abcde"

words = ["a", "bb", "acd", "ace"]

那么首先我們將words數組中的單詞按照其首字母的不同放入對應的群組中,得到:

a -> {0, 1}, {2, 1}, {3, 1}

b -> {1, 1}

這里,每個pair的第一個數字是該單詞在words中的位置,第二個數字是下一個字母的位置。比如 {0, 1} 表示 "a" 在words數組中位置為0,且下一個位置為1(因為當前位置是首字母)。{2, 1} 表示 "acd" 在words數組中位置為2,且下一個位置為1。{3, 1} 表示 "ace" 在words數組中位置為3,且下一個位置為1。{1, 1} 表示 "bb" 在words數組中位置為1,且下一個位置為1。

好,下面我們來遍歷字符串S,第一個遇到的字母是 'a'。

那么我們群組中a對應了三個pair,將其提取出來分別進行操作。首先處理 {0, 1},此時我們發現下一個位置為1,和單詞"a"的長度相同了,說明是子序列,結果res自增1。然后處理 {2, 1},在"acd"中取下一個位置1的字母為'c',則將下一位置自增1后的新pair {2, 2} 加入c對應的群組。然后處理 {3, 1},在"ace"中取下一個位置1的字母為'c',則將下一位置自增1后的新pair {3, 2} 加入c對應的群組。則此時的群組為:

b -> {1, 1}

c -> {2, 2}, {3, 2}

好,繼續來遍歷字符串S,第二個遇到的字母是 'b'。

那么我們群組中b對應了一個pair,處理 {1, 1},在"bb"中取下一個位置1的字母為'b',則將下一位置自增1后的新pair {1, 2} 加入b對應的群組。則此時的群組為:

b -> {1, 2}

c -> {2, 2}, {3, 2}

好,繼續來遍歷字符串S,第三個遇到的字母是 'c'。

那么我們群組中c對應了兩個pair,將其提取出來分別進行操作。首先處理 {2, 2},在"ace"中取下一個位置2的字母為'e',則將下一位置自增1后的新pair {2, 3} 加入e對應的群組。然后處理 {3, 2},在"acd"中取下一個位置2的字母為'd',則將下一位置自增1后的新pair {3, 3} 加入d對應的群組。則此時的群組為:

b -> {1, 2}

d -> {3, 3}

e -> {2, 3}

好,繼續來遍歷字符串S,第四個遇到的字母是 'd'。

那么我們群組中d對應了一個pair,處理 {3, 3},此時我們發現下一個位置為3,和單詞"acd"的長度相同了,說明是子序列,結果res自增1。則此時的群組為:

b -> {1, 2}

e -> {2, 3}

好,繼續來遍歷字符串S,第五個遇到的字母是 'e'。

那么我們群組中e對應了一個pair,處理 {2, 3},此時我們發現下一個位置為3,和單詞"ace"的長度相同了,說明是子序列,結果res自增1。則此時的群組為:

b -> {1, 2}

此時S已經遍歷完了,已經沒有b了,說明"bb"不是子序列,這make sense,返回結果res即可,參見代碼如下:

 

解法二:

class Solution {
public:
    int numMatchingSubseq(string S, vector<string>& words) {
        vector<pair<int, int>> all[128];
        int res = 0, n = words.size();
        for (int i = 0; i < n; i++) {
            all[words[i][0]].emplace_back(i, 1);
        }
        for (char c : S) {
            auto vect = all[c];
            all[c].clear();
            for (auto it : vect) {
                if (it.second == words[it.first].size()) ++res;
                else all[words[it.first][it.second++]].push_back(it);
            }
        }
        return res;
    }
};

 

類似題目:

Is Subsequence

 

參考資料:

https://leetcode.com/problems/number-of-matching-subsequences/solution/

https://leetcode.com/problems/number-of-matching-subsequences/discuss/117634/Efficient-and-simple-go-through-words-in-parallel-with-explanation

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM