LeetCode 每日一题1178. 猜字谜
作者:互联网
1178. 猜字谜
外国友人仿照中国字谜设计了一个英文版猜字谜小游戏,请你来猜猜看吧。
字谜的迷面 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]
所包含的字符都不重复。
方法一:状态压缩 + 枚举
解题思路
此题的暴力解法不难想到,对于每一个谜面 puzzle
都去遍历 words
求解。
根据提示:谜面最大值 10^4
、谜底最大值 10^5
,相乘为 10^9
,一定会超时。
所以,需要思考其他解法:
- 根据提示,谜面和谜底都只包含小写字母,且我们并不关心重复的字母。对于任一谜面或谜底,我们只关心某个字母是否出现过,这样我们可以把字符串用
26
位bit
来表示,从左往右分别表示字母z
到a
,值为 0 表示未出现,1 表示出现过。 - Java 中 int 有 32 位,可以直接使用 int 的后 26 位来表示字符串。
理解了上述压缩过程,我们来看具体的实现步骤:
- 先把谜底
words
中的字符串转为数字,不同的word
可能转成相同的数字,如“ab”
和“aaabbb”
转成数字都是3
。因此,需要一个 map 统计每个数字的频率。 - 遍历谜面
puzzles
,同样的把谜面转成数字。根据提示谜面只有 7 位,也就是转成的数字的二进制位只有 7 个 1。我们可以 枚举 所有子集(27 - 1),对于任一子集只要 包含首字母且 map 中存在 就是有效的。
细节:
subnum = (subnum - 1) & bitnum
用于枚举bitnum
所有子集,可以试着画图理解这句代码。
参考代码
public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
// 把 words 中的字符串转为数字,并统计数字的出现次数。
Map<Integer, Integer> freq = new HashMap<>();
for (String word : words) {
int bitnum = getBitnum(word);
freq.put(bitnum, freq.getOrDefault(bitnum, 0) + 1);
}
List<Integer> ans = new ArrayList<>();
for (String puzzle : puzzles) {
int bitnum = getBitnum(puzzle);
int first = 1 << (puzzle.charAt(0) - 'a');
int subnum = bitnum;
int count = 0;
// 枚举谜面的所有子集:包含首字母 && map中存在
while (subnum > 0) {
if ((subnum & first) > 0 && freq.containsKey(subnum)) {
count += freq.get(subnum);
}
subnum = (subnum - 1) & bitnum;
}
ans.add(count);
}
return ans;
}
public Integer getBitnum(String s) {
int bitnum = 0;
for (char ch : s.toCharArray()) {
bitnum |= (1 << (ch - 'a'));
}
return bitnum;
}
执行结果
标签:puzzles,谜底,bitnum,1178,单词,谜面,猜字谜,words,LeetCode 来源: https://blog.csdn.net/qq_27007509/article/details/114125796