[LeetCode] 1178. Number of Valid Words for Each Puzzle 猜字謎



With respect to a given puzzle string, a word is valid if both the following conditions are satisfied:

  • word contains the first letter of puzzle.
  • For each letter in word, that letter is in puzzle.
    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 array answer, where answer[i] is the number of words in the given word list words that are valid with respect to the puzzle puzzles[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 word for "aboveyz" : "aaaa"
1 valid word for "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're no 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/

https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/discuss/372385/Java-Bit-manipulation-%2B-Map-Solution-90ms

https://leetcode.com/problems/number-of-valid-words-for-each-puzzle/discuss/372145/Python-Bit-manipulation-detailed-explanation


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


免責聲明!

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



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