【搜索难题】力扣1178-猜字谜

  |  

摘要: 一个 bitest 的应用题

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


各位好,在文章 位运算操作 中,我们学习了位运算常见的操作。用位运算表示集合操作的时候,有时集合元素非常多,超出了整数的位数范围,在 【STL】bitset 中提到的 bitset 就是一种替代的方法。本文我们看一个题目,用 bitset 表示大量元素的集合,然后用位运算操作枚举子集。

$1 题目

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

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

单词 word 中包含谜面 puzzle 的第一个字母。
单词 word 中的每一个字母都可以在谜面 puzzle 中找到。
例如,如果字谜的谜面是 “abcdefg”,那么可以作为谜底的单词有 “faced”, “cabbage”, 和 “baggage”;而 “beefed”(不含字母 “a”)以及 “based”(其中的 “s” 没有出现在谜面中)。
返回一个答案数组 answer,数组中的每个元素 answer[i] 是在给出的单词列表 words 中可以作为字谜迷面 puzzles[i] 所对应的谜底的单词数目。

提示:

1
2
3
4
5
6
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] 所包含的字符都不重复。

示例:
输入:
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’。


$2 题解

算法 : bitset

words 中的单词,每个单词按照出现的字符可以表示为一个 bitset,bitset 相同的单词,在后续的计数中是等效的。

用哈希表记录 words 中各个 bitset 出现的次数。mapping[bitset] := words 中具有 bitset 表示的字符集合的 word 个数

枚举谜面 puzzles[i],求出谜面的 bitset,记为 s。枚举 s 的所有子集,如果该子集含有谜面的第一个字符,就在 mapping 中找到相应的计数,累加到 result[i] 上。

枚举一个集合 S 的真子集:

1
2
3
4
for(subset = (A - 1) & A; subset != A; subset = (subset - 1) & A)
{
...
}

代码 (C++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
unordered_map<int, int> mapping; // bitmap 模式 -> 单词个数
for(const string &w: words)
{
int pattern = 0;
for(const char &ch: w)
pattern |= (1 << (ch - 'a'));
++mapping[pattern];
}
int m = puzzles.size();
vector<int> result(m);
for(int i = 0; i < m; ++i)
{
int s = 0;
for(const char &ch: puzzles[i])
s |= 1 << (ch - 'a');
result[i] += mapping[s];
// 枚举 s 的真子集
for(int subset = (s - 1) & s; subset != s; subset = (subset - 1) & s)
{
if(subset & (1 << (puzzles[i][0] - 'a')))
result[i] += mapping[subset];
}
}
return result;
}
};

Share