-
Notifications
You must be signed in to change notification settings - Fork 0
/
jewels_and_stones.py
109 lines (87 loc) · 3.99 KB
/
jewels_and_stones.py
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python3
# Jewels and Stones
#
# https://leetcode.com/problems/jewels-and-stones
#
# You're given strings jewels representing the types of stones that are jewels,
# and stones representing the stones you have. Each character in stones is a
# type of stone you have. You want to know how many of the stones you have are
# also jewels.
# Letters are case sensitive, so "a" is considered a different type of stone
# from "A".
def test():
"""
Run `pytest <this-file>`.
"""
def test_algo(algo):
assert algo(jewels="aA", stones="aAAbbbb") == 3
assert algo(jewels="z", stones="ZZ") == 0
# Test all different algorithms/implementations
solution = Solution()
for algo in [solution.brute_force, solution.hash_set, solution.regex]:
test_algo(algo)
class Solution:
def brute_force(self, jewels: str, stones: str) -> int:
"""
Approach: Brute-force.
Idea: Keep only the stones that are jewels in a filtering pass, and count them.
Time: O(n*m): For each of the n stones, check whether it is a jewel (O(m)).
Space: O(1): No additional memory is used.
Leetcode: 39 ms runtime, 16.43 MB memory
"""
def is_jewel(stone: str) -> bool:
"""Check if a stone is a jewel."""
for jewel in jewels:
if stone == jewel:
return True
return False
def count(iter) -> int:
"""Count the number of elements in a iterator without collecting."""
return sum(1 for _ in iter)
return count(filter(is_jewel, stones))
def hash_set(self, jewels: str, stones: str) -> int:
"""
Approach: Use hashset.
Idea: Keep only the stones that are jewels in a filtering pass, and count them.
Time: O(n): For each of the n stones, check whether it is a jewel (O(1) due to hashset contains efficiency).
Space: O(m): Store the m jewels as a hashset.
Leetcode: 39 ms runtime, 16.51 MB memory
"""
jewels_set = set(jewels)
def is_jewel(stone: str) -> bool:
"""Check if a stone is a jewel."""
return stone in jewels_set
def count(iter) -> int:
"""Count the number of elements in a iterator without collecting."""
return sum(1 for _ in iter)
return count(filter(is_jewel, stones))
def regex(self, jewels: str, stones: str) -> int:
"""
Approach: Use regex.
Idea: Remove all non-jewels using regex, and count remaining.
Time: O(n): Assume regex substitution takes O(n) for n stones.
Space: O(m): Store the all jewels in a string (at most size m).
Leetcode: 52 ms runtime, 16.49 MB memory
"""
import re
# Remove all non-jewels.
jewels_only = re.sub(rf"[^{jewels}]", "", stones)
return len(jewels_only)
def occurences_in_string(self, jewels: str, stones: str) -> int:
"""
Approach: Count jewel occurences in stones.
Idea: For each jewel, count its number of occurences in the stones string, and return the total sum.
Time: O(n*m): For each of the m jewels, count the number of times it occurs in the stones string (O(n)).
Space: O(1): No additional memory is used.
Leetcode: 47 ms runtime, 16.54 MB memory
"""
return sum(map(lambda jewel: stones.count(jewel), jewels))
def occurences_in_string_2(self, jewels: str, stones: str) -> int:
"""
Approach: Count stone occurences in jewels.
Idea: For each stone, count its number of occurences in the jewels string, and return the total sum.
Time: O(n*m): For each of the n stones, count the number of times it occurs in the jewels string (O(m)).
Space: O(1): No additional memory is used.
Leetcode: 30 ms runtime, 16.40 MB memory
"""
return sum(map(lambda stone: jewels.count(stone), stones))