|
| 1 | +--- |
| 2 | +id: number-of-matching-subsequences |
| 3 | +title: Number of Matching Subsequences |
| 4 | +sidebar_label: 792 - Number of Matching Subsequences |
| 5 | +tags: [Trie, Two Pointers, Hash Table, C++, Java, Python] |
| 6 | +description: Solve the problem of counting the number of words that are subsequences of a given string using various efficient methods. |
| 7 | +--- |
| 8 | + |
| 9 | +## Problem Statement |
| 10 | + |
| 11 | +### Problem Description |
| 12 | + |
| 13 | +You are given a string `s` and an array of strings `words`. The task is to return the number of `words[i]` that are subsequences of `s`. |
| 14 | + |
| 15 | +A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters. |
| 16 | + |
| 17 | +For example, "ace" is a subsequence of "abcde". |
| 18 | + |
| 19 | +### Example |
| 20 | + |
| 21 | +**Example 1:** |
| 22 | +``` |
| 23 | +Input: s = "abcde", words = ["a","bb","acd","ace"] |
| 24 | +Output: 3 |
| 25 | +``` |
| 26 | + |
| 27 | +**Explanation:** There are three strings in `words` that are subsequences of `s`: "a", "acd", "ace". |
| 28 | + |
| 29 | +**Example 2:** |
| 30 | +``` |
| 31 | +Input: s = "dsahjpjauf", words = ["ahjpjau","ja","ahbwzgqnuk","tnmlanowax"] |
| 32 | +Output: 2 |
| 33 | +``` |
| 34 | + |
| 35 | +### Constraints |
| 36 | + |
| 37 | +- $1 \leq \text{s.length} \leq 5 \times 10^4$ |
| 38 | +- $1 \leq \text{words.length} \leq 5000$ |
| 39 | +- $1 \leq \text{words[i].length} \leq 50$ |
| 40 | +- `s` and `words[i]` consist of only lowercase English letters. |
| 41 | + |
| 42 | +## Solution |
| 43 | + |
| 44 | +### Intuition |
| 45 | + |
| 46 | +To efficiently count the number of subsequences, we can use different methods, including Trie, Two Pointers, and Hash Table. The goal is to check for each word in `words` whether it can be formed as a subsequence of `s`. |
| 47 | + |
| 48 | +### Methods |
| 49 | + |
| 50 | +#### Method 1: Two Pointers |
| 51 | + |
| 52 | +For each word in `words`, we can use two pointers to check if it is a subsequence of `s`. One pointer iterates through `s`, and the other iterates through the current word. We match characters and advance both pointers accordingly. |
| 53 | + |
| 54 | +**Time Complexity:** |
| 55 | +The time complexity is $O(n \cdot m)$, where `n` is the length of `s` and `m` is the total number of characters in `words`. |
| 56 | + |
| 57 | +#### Method 2: Hash Table and Bucketing |
| 58 | + |
| 59 | +We can use a hash table where each key is a character from `a` to `z` and each value is a list of iterators. For each character in `s`, we update the hash table to see if there are any words that can progress to the next character. |
| 60 | + |
| 61 | +**Time Complexity:** |
| 62 | +The time complexity is $O(n + m)$, where `n` is the length of `s` and `m` is the total length of all words in `words`. |
| 63 | + |
| 64 | +### Code |
| 65 | + |
| 66 | +#### C++ |
| 67 | + |
| 68 | +```cpp |
| 69 | +class Solution { |
| 70 | +public: |
| 71 | + int numMatchingSubseq(string s, vector<string>& words) { |
| 72 | + vector<vector<int>> waiting(128); |
| 73 | + for (int i = 0; i < words.size(); ++i) { |
| 74 | + waiting[words[i][0]].push_back(i); |
| 75 | + } |
| 76 | + |
| 77 | + int res = 0; |
| 78 | + for (char c : s) { |
| 79 | + vector<int> advance = move(waiting[c]); |
| 80 | + waiting[c].clear(); |
| 81 | + for (int idx : advance) { |
| 82 | + if (++words[idx].length() == words[idx].size()) { |
| 83 | + ++res; |
| 84 | + } else { |
| 85 | + waiting[words[idx][words[idx].length()]].push_back(idx); |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + return res; |
| 90 | + } |
| 91 | +}; |
| 92 | +``` |
| 93 | +#### Java |
| 94 | +```java |
| 95 | +class Solution { |
| 96 | + public int numMatchingSubseq(String s, String[] words) { |
| 97 | + List<int[]>[] waiting = new ArrayList[128]; |
| 98 | + for (int c = 0; c <= 127; ++c) { |
| 99 | + waiting[c] = new ArrayList<>(); |
| 100 | + } |
| 101 | + for (int i = 0; i < words.length; ++i) { |
| 102 | + waiting[words[i].charAt(0)].add(new int[]{i, 1}); |
| 103 | + } |
| 104 | + |
| 105 | + int res = 0; |
| 106 | + for (char c : s.toCharArray()) { |
| 107 | + List<int[]> advance = waiting[c]; |
| 108 | + waiting[c] = new ArrayList<>(); |
| 109 | + for (int[] arr : advance) { |
| 110 | + if (arr[1] == words[arr[0]].length()) { |
| 111 | + ++res; |
| 112 | + } else { |
| 113 | + waiting[words[arr[0]].charAt(arr[1]++)].add(arr); |
| 114 | + } |
| 115 | + } |
| 116 | + } |
| 117 | + return res; |
| 118 | + } |
| 119 | +} |
| 120 | +``` |
| 121 | +#### Python |
| 122 | +```python |
| 123 | +class Solution: |
| 124 | + def numMatchingSubseq(self, s: str, words: List[str]) -> int: |
| 125 | + from collections import defaultdict |
| 126 | + waiting = defaultdict(list) |
| 127 | + for i, word in enumerate(words): |
| 128 | + waiting[word[0]].append((i, 1)) |
| 129 | + |
| 130 | + res = 0 |
| 131 | + for c in s: |
| 132 | + advance = waiting[c] |
| 133 | + waiting[c] = [] |
| 134 | + for i, j in advance: |
| 135 | + if j == len(words[i]): |
| 136 | + res += 1 |
| 137 | + else: |
| 138 | + waiting[words[i][j]].append((i, j + 1)) |
| 139 | + return res |
| 140 | +``` |
0 commit comments