加载中...
1178-猜字谜(Number of Valid Words for Each Puzzle)
发表于:2021-12-03 | 分类: 困难
字数统计: 699 | 阅读时长: 3分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/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" (does not include 'a') and "based" (includes 's' which is not in the puzzle).
Return an array answer, where answer[i] is the number of words in the given word list words that is valid with respect to the puzzle puzzles[i].

 

Example 1:

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 are no valid words for "gaswxyz" cause none of the words in the list contains letter 'g'.

Example 2:

Input: words = ["apple","pleas","please"], puzzles = ["aelwxyz","aelpxyz","aelpsxy","saelpxy","xaelpsy"]
Output: [0,1,3,2,0]

 

Constraints:

  • 1 <= words.length <= 105
  • 4 <= words[i].length <= 50
  • 1 <= puzzles.length <= 104
  • puzzles[i].length == 7
  • words[i] and puzzles[i] consist of lowercase English letters.
  • Each puzzles[i] does not contain repeated characters.

中文题目

外国友人仿照中国字谜设计了一个英文版猜字谜小游戏,请你来猜猜看吧。

字谜的迷面 puzzle 按字符串形式给出,如果一个单词 word 符合下面两个条件,那么它就可以算作谜底:

  • 单词 word 中包含谜面 puzzle 的第一个字母。
  • 单词 word 中的每一个字母都可以在谜面 puzzle 中找到。
    例如,如果字谜的谜面是 "abcdefg",那么可以作为谜底的单词有 "faced", "cabbage", 和 "baggage";而 "beefed"(不含字母 "a")以及 "based"(其中的 "s" 没有出现在谜面中)都不能作为谜底。

返回一个答案数组 answer,数组中的每个元素 answer[i] 是在给出的单词列表 words 中可以作为字谜迷面 puzzles[i] 所对应的谜底的单词数目。

 

示例:

输入:
words = ["aaaa","asas","able","ability","actt","actor","access"], 
puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
输出:[1,1,3,2,4,0]
解释:
1 个单词可以作为 "aboveyz" 的谜底 : "aaaa" 
1 个单词可以作为 "abrodyz" 的谜底 : "aaaa"
3 个单词可以作为 "abslute" 的谜底 : "aaaa", "asas", "able"
2 个单词可以作为 "absoryz" 的谜底 : "aaaa", "asas"
4 个单词可以作为 "actresz" 的谜底 : "aaaa", "asas", "actt", "access"
没有单词可以作为 "gaswxyz" 的谜底,因为列表中的单词都不含字母 'g'。

 

提示:

  • 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] 都是小写英文字母。
  • 每个 puzzles[i] 所包含的字符都不重复。

通过代码

高赞题解

各位题友大家好! 今天是 @负雪明烛 坚持日更的第 33 天。今天力扣上的每日一题是「1178. 猜字谜」。

解题思路

本文的两个重点:

  • 把每个字符串用二进制数字表示(状态压缩)
  • 寻找所有子集(subset)

大家好,今天的题虽然是 Hard,但是大家不要怕,本题解把难度降为了 Medium,肯定让你看懂。

首先让所有 wordspuzzle 两两匹配肯定是不行的,时间复杂度到了 $O(M * N) = 10 ^ 9$,会超时。

一个简单的思路是,对于每个 puzzle 没有必要遍历所以 words,只用找符合条件的 words 出现了多少次就行了。这就是很多题解的思路:状态压缩。

题目给出了两个条件:

  • 单词 word 中包含谜面 puzzle 的第一个字母。
  • 单词 word 中的每一个字母都可以在谜面 puzzle 中找到。

第一步:状态压缩

注意题目的第二个条件只要求能找到(出现一次即可),对出现次数没要求。为了解决这个问题,我们可以使用二进制数字来表每个 wordpuzzle,该二进制数字就是 wordpuzzle 的特征。这道题只包含 26 个小写字母,可以压缩到一个 int 中。int 中的每一位取01表示字符是否出现过。比如 “aabb” 可以用 11 表示,”accc” 可以用 101 表示。

可以看出不同的单词可能映射成同一个数字,比如 “aabbb” 和 “ab” 都映射成了 11。这就是状态压缩。

1178.001.jpeg

把每个 word 都做状态压缩,并用 hashmap 保存每个二进制数字出现的次数。

第二步:匹配

根据题目的两个条件,puzzle 的第一个字符必须跟 word 的第一个字符相同;word 中每一个字符都要在 puzzle 中找到,所以要找的是 word 状态压缩后的数字 和 puzzle[0] + subset(puzzle[1:N - 1]) 状态压缩后的数字相等。

很多题解都在讨论二进制表示下的 subset 怎么求,我觉得都不好理解,直接做一下「78. 子集」不就得了?暴力求出puzzle[1:N - 1]的所有子集,然后计算 puzzle[0] + subset(puzzle[1:N - 1]) 对应的状态。

题目说了 puzzle 的长度为 7 位,subset(puzzle[1:N - 1]) 的是时间复杂度为 $O(2 ^ N)$ = $2 ^ 6 = 64$ 次计算,比较快。

求出puzzle[0] + subset(puzzle[1:N - 1]) 对应的二进制数字之后,累加 hashmap 中该二进制数字出现的次数,就是该 puzzle 对应的 word 有多少。

代码

Python 代码如下,直接用了 78 题的求 subset 代码。

class Solution:
    def findNumOfValidWords(self, words: List[str], puzzles: List[str]) -> List[int]:
        freq = collections.Counter()
        for word in words:
            mask = 0
            for c in word:
                mask |= 1 << (ord(c) - ord('a'))
            freq[mask] += 1
        res = []
        for puzzle in puzzles:
            total = 0
            for perm in self.subsets(puzzle[1:]):
                mask = 1 << (ord(puzzle[0]) - ord('a'))
                for c in perm:
                    mask |= 1 << (ord(c) - ord('a'))
                total += freq[mask]
            res.append(total)
        return res
    
    def subsets(self, words: List[int]) -> List[List[int]]:
        res = [""]
        for i in words:
            res = res + [i + word for word in res]
        return res
  • 时间复杂度:$O(M + N)$。
  • 空间复杂度:$O(M)$。

刷题心得

不畏浮云遮望眼,透过现象看本质。


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

欢迎关注我的公众号:每日算法题

祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!

统计信息

通过次数 提交次数 AC比率
18800 40655 46.2%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1177-构建回文串检测(Can Make Palindrome from Substring)
下一篇:
1390-四因数(Four Divisors)
本文目录
本文目录