[LeetCode] 1178. Number of Valid Words for Each Puzzle 猜字謎
With respect to a givenpuzzle
string, aword
isvalidif both the following conditions are satisfied:
word
contains the first letter ofpuzzle
.- For each letter in
word
, that letter is inpuzzle
.
For example, if the puzzle is "abcdefg", then valid words are "faced", "cabbage", and "baggage"; while invalid words are "beefed" (doesn't include "a") and "based" (includes "s" which isn't in the puzzle).
Return an arrayanswer
, whereanswer[i]
is the number of words in the given word listwords
that are valid with respect to the puzzlepuzzles[i]
.
Example :
Input: words = ["aaaa","asas","able","ability","actt","actor","access"], puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"] Output: [1,1,3,2,4,0] Explanation: 1 valid wordfor "aboveyz" : "aaaa" 1 valid wordfor "abrodyz" : "aaaa" 3 valid words for "abslute" : "aaaa", "asas", "able" 2 valid words for"absoryz" : "aaaa", "asas" 4 valid words for"actresz" : "aaaa", "asas", "actt", "access" There'reno valid words for"gaswxyz" cause none of the words in the list contains letter 'g'.
Constraints:
1 <= words.length <= 10^5
4 <= words[i].length <= 50
1 <= puzzles.length <= 10^4
puzzles[i].length == 7
words[i][j]
,puzzles[i][j]
are English lowercase letters.- Each
puzzles[i]
doesn't contain repeated characters.
這道題說對於一個 puzzle 字串,當滿足兩個條件就表示某個 word 是合法的。第一個是當 word 包含 puzzle 的首字母,第二個是對於 word 中的所有字母,均在 puzzle 中出現(這裡不考慮次數,只考慮字母種類)。現在給了一個單詞陣列,和一個謎語陣列,問對於每個 puzzle,各有多少個單詞是合法的。這道題的題目挺長的,但是好在給的例子有詳細的解釋,題意並不難理解。大家能想到的最簡單暴力的破解方法就是對於每個 puzzle,都遍歷一遍所有的單詞,去驗證給的兩個條件是否成立。但這種方法根本對不住本題的 Hard 身價,會被 OJ 無情拒絕,所以必須想更高效巧妙的方法。先從兩個條件入手,第一個需要包含 puzzle 的首字母,這個沒啥說的,很好滿足,主要是第二個條件,word 中所有字母都需要在 puzzle 中出現,暴力搜尋中的遍歷會很耗時。因為這裡不考慮出現次數,所以不用建立次數對映,而只是考慮字母種類的話,就可以用位操作 Bit Manipulation 來做,因為只有 26 個字母,若用每一個位來表示對應的字母,1表示存在,0表示不存在,那麼最多隻需要大小為 2^26 - 1 的整型數就能表示所有的情況。這樣每個單詞都可以用一個 mask 來表示了,注意不同的單詞可能會有相同的 mask,比如 apple 和 pale,但不影響做題,因為字母的次數和順序都無關緊要。由於這裡關心的是相同 mask 的個數,則可以用個 HashMap 來建立 mask 和其出現次數之間的對映,方便之後的查詢。
接下里就要開始處理 puzzles 陣列了,遍歷每個 puzzle 字串,同樣需要統計其字母種類,即算出 mask。跟 puzzle 的 mask 相同的單詞肯定都是滿足題意的,但不僅僅是相同的,還有 mask 的嚴格子集也是滿足題意的,比如 puzzle 是 apple 的話,那麼 word 可以是 apple,也可以是 apple 的子集,比如 app,ap,al 等等,只要包含首字母就可以了。那麼如何求子集的 mask 呢,由於子集是減少字母的,所以 mask 值一定比原來的小,所以可以每次減1,但由於只能將原來1的位置變為0,而不能把0變為1,所以還需要 '與' 上原來的 mask。這樣就可以使用一個 while 迴圈,若當前 sub(即為 mask)'與' 上 first 還是 first,且 sub 在 maskMap 中存在,則將其的對映值累加到 cnt。若此時 sub 為0,則 break 掉迴圈,否則 sub 自減1並 '與' 上原 mask 即可,參見程式碼如下:
class Solution {
public:
vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
vector<int> res;
unordered_map<int, int> maskMap;
for (string word : words) {
int mask = 0;
for (char c : word) {
mask |= 1 << (c - 'a');
}
++maskMap[mask];
}
for (string puzzle : puzzles) {
int mask = 0;
for (char c : puzzle) {
mask |= 1 << (c - 'a');
}
int sub = mask, cnt = 0, first = 1 << (puzzle[0] - 'a');
while (true) {
if ((sub & first) == first && maskMap.count(sub)) {
cnt += maskMap[sub];
}
if (sub == 0) break;
sub = (sub - 1) & mask;
}
res.push_back(cnt);
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1178
參考資料:
https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/