032 · Jewels and Stones
My First Thoughts
这道题我看看哈,两个字符串 jewels 和 stones。
实际就是一个单层问题:stones 里有多少个字符出现在 jewels 中,对吧?并且题目说区分大小写,所以 "a" 和 "A" 要当成两个不同字符。
最直接的写法就是准备一个计数器 total,然后遍历 stones 里的每个字符。如果这个字符在 jewels 里,就把 total 加一:
def jewelsstones(jewels, stones):
total = 0
for char in stones:
if char in jewels:
total += 1
return total这个思路是对的,而且在这道题里已经够用。因为题目只是在问“有多少块石头也是宝石”,所以每块石头看一次,能数就数,不能数就跳过。
这里还可以延申一下:如果题目变成“不考虑大小写”,那就可以把两边都转成小写再比较。这个方向也合理,只是 Python 里调用 lower 要带括号:
def jewelsstones_ignore_case(jewels, stones):
total = 0
jewels_lower = jewels.lower()
for char in stones:
if char.lower() in jewels_lower:
total += 1
return total不过这是延申版本,不是原题版本。原题明确区分大小写,所以最终解法不能把字符统一转小写。
Why That Is Not Enough
直接写:
if char in jewels:逻辑上没有问题。这道题的约束也很小,jewels 和 stones 最长都是 50,所以直接查字符串也能通过。
真正可以改进的地方是:这句代码背后的操作是“判断一个字符是否属于宝石类型”。如果 jewels 是字符串,那么每次 char in jewels 都可能要在字符串里从头找一遍。
比如:
jewels = "abcdef"
char = "f"
为了确认 "f" 在不在里面,可能要看过 "a"、"b"、"c"、"d"、"e",最后才找到 "f"。
但这道题里,jewels 更像一个集合:它表示“哪些字符是宝石”。当我们反复问:
这个字符是不是宝石?
用 set 更自然。set 的目的就是快速回答“某个东西在不在这一组东西里”。所以最终代码可以在你的直接循环基础上只改一处:先把 jewels 转成 set,后面查 jewel_set。
Final Idea
关键思路是:
先把所有宝石类型放进
set,再遍历stones统计哪些石头出现在这个集合里。
从你的第一版开始:
if char in jewels:把右边的 jewels 换成一个提前准备好的集合:
jewel_set = set(jewels)
if char in jewel_set:set(jewels) 会把字符串拆成一个个字符,放进集合里。比如:
set("aA")得到的意思就是:
{"a", "A"}
然后遍历 stones:
看到 "a" -> 在集合里,total += 1
看到 "A" -> 在集合里,total += 1
看到 "b" -> 不在集合里,跳过
这样代码仍然是很直观的计数逻辑,只是 membership check 从“在字符串里找”变成了“在集合里查”。
Why It Works
jewel_set 包含了所有宝石类型。因为题目说 jewels 中的字符互不相同,所以把它转成 set 不会丢掉任何有效信息。
接下来,stones 中每个字符都代表一块石头。对每块石头来说,只有两种情况:
- 它在
jewel_set里,说明这块石头是宝石,计数加一 - 它不在
jewel_set里,说明这块石头不是宝石,计数不变
遍历完所有 stones 后,total 就正好等于宝石石头的数量。
大小写也会自然保持正确。因为 Python 的字符串字符本来就区分大小写,"z" 和 "Z" 是不同字符。只要我们不调用 lower() 或 upper(),示例 2 里的 jewels = "z"、stones = "ZZ" 就会返回 0。
Code
def numJewelsInStones(jewels, stones):
jewel_set = set(jewels)
total = 0
for char in stones:
if char in jewel_set:
total += 1
return total也可以写成更短的版本:
def numJewelsInStones(jewels, stones):
jewel_set = set(jewels)
return sum(char in jewel_set for char in stones)不过初学时第一版更清楚:total 表示当前已经数到的宝石数量,每遇到一块宝石就加一。
Complexity
| Time | \(O(j + s)\) - j 是 jewels 的长度,s 是 stones 的长度;先建集合,再遍历所有石头 |
| Space | \(O(j)\) - jewel_set 里最多保存 jewels 中的所有字符 |
Takeaway
如果题目反复问“某个东西在不在这一组东西里”,
set通常是很自然的工具。直接用字符串查找在这道题里也能过,但把jewels转成set后,代码的含义更贴近题目:jewel_set就是所有宝石类型的集合,char in jewel_set就是在问“这块石头是不是宝石”。
← Quiz