Skip to content

Commit 5b5bb62

Browse files
authored
Merge pull request #182 from BrianLusina/feat/datastructures-hashmap
feat(datastructures, hashmap): design a hashmap
2 parents 98bf909 + 060ad3c commit 5b5bb62

29 files changed

+513
-179
lines changed

DIRECTORY.md

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@
198198
* Hash Table
199199
* First Unique Character
200200
* [Test First Unique Character](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/first_unique_character/test_first_unique_character.py)
201+
* Jewels And Stones
202+
* [Test Jewels And Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/jewels_and_stones/test_jewels_and_stones.py)
201203
* Ransom Note
202204
* [Test Ransom Note](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/ransom_note/test_ransom_note.py)
203205
* Heap
@@ -274,14 +276,56 @@
274276
* [Test Check Subarray Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/prefix_sum/continous_sub_array_sum/test_check_subarray_sum.py)
275277
* Search
276278
* Binary Search
279+
* Big Words
280+
* [Big Words](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/big_words/big_words.py)
281+
* Cyclically Shifted Array
282+
* [Test Cyclically Shifted Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/cyclically_shifted_array/test_cyclically_shifted_array.py)
277283
* Divide Chocolate
278284
* [Test Divide Chocolate](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/divide_chocolate/test_divide_chocolate.py)
285+
* Find Closest Number
286+
* [Test Find Closest Number](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/find_closest_number/test_find_closest_number.py)
287+
* Find Closest Value
288+
* [Test Find Closest Value](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/find_closest_value/test_find_closest_value.py)
289+
* Find First In Duplicate List
290+
* [Test Find First In Duplicates](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/find_first_in_duplicate_list/test_find_first_in_duplicates.py)
291+
* Find Fixed Number
292+
* [Test Find Fixed Number](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/find_fixed_number/test_find_fixed_number.py)
293+
* Find Peak Element
294+
* [Test Find Peak Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/find_peak_element/test_find_peak_element.py)
295+
* First Boundary
296+
* [Test First Boundary](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/first_boundary/test_first_boundary.py)
297+
* First Occurrence
298+
* [Test Find First Occurrence](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/first_occurrence/test_find_first_occurrence.py)
299+
* Integer Square Root
300+
* [Test Integer Square Root](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/integer_square_root/test_integer_square_root.py)
301+
* Koko Eating Bananas
302+
* [Test Koko Eating Bananas](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/koko_eating_bananas/test_koko_eating_bananas.py)
279303
* Magnetic Force Between Two Balls
280304
* [Test Magnetic Force Between Two Balls](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/magnetic_force_between_two_balls/test_magnetic_force_between_two_balls.py)
281305
* Maxruntime N Computers
282306
* [Test Max Runtime](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/maxruntime_n_computers/test_max_runtime.py)
307+
* Min In Rotated Sorted Array
308+
* [Test Find Min Rotated Sorted Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/min_in_rotated_sorted_array/test_find_min_rotated_sorted_array.py)
309+
* My Calendar
310+
* [Test My Calendar](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/my_calendar/test_my_calendar.py)
311+
* Next Greatest Letter
312+
* [Test Next Greatest Letter](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/next_greatest_letter/test_next_greatest_letter.py)
313+
* Peak Of Mountain
314+
* [Test Peak Of Mountain](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/peak_of_mountain/test_peak_of_mountain.py)
315+
* Plates Between Candles
316+
* [Test Plates Between Candles](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/plates_between_candles/test_plates_between_candles.py)
317+
* Rotated Sorted Array
318+
* [Test Search Rotated Sorted Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/rotated_sorted_array/test_search_rotated_sorted_array.py)
319+
* Search Range
320+
* [Test Search Range](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/search_range/test_search_range.py)
321+
* Single Non Duplicate
322+
* [Test Single Non Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/single_non_duplicate/test_single_non_duplicate.py)
283323
* Split Array Largest Sum
284324
* [Test Split Array Largest Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/split_array_largest_sum/test_split_array_largest_sum.py)
325+
* Square Root
326+
* [Test Sqrt Estimate](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/square_root/test_sqrt_estimate.py)
327+
* Successful Pairs Spells Potions
328+
* [Test Successful Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/successful_pairs_spells_potions/test_successful_pairs.py)
285329
* [Test Binary Search](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/binary_search/test_binary_search.py)
286330
* Interpolation
287331
* [Test Interpolation Search](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/search/interpolation/test_interpolation_search.py)
@@ -326,6 +370,11 @@
326370
* [Test Daily Temperatures](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/stack/daily_temperatures/test_daily_temperatures.py)
327371
* Minimum String Length After Removing Substrings
328372
* [Test Min Str Length After Removing Substrings](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/stack/minimum_string_length_after_removing_substrings/test_min_str_length_after_removing_substrings.py)
373+
* Strings
374+
* Caeser Cipher
375+
* [Test Caeser](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/strings/caeser_cipher/test_caeser.py)
376+
* Run Length Encoding
377+
* [Test Run Length](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/strings/run_length_encoding/test_run_length.py)
329378
* Taxi Numbers
330379
* [Taxi Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/taxi_numbers/taxi_numbers.py)
331380
* Top K Elements
@@ -475,6 +524,9 @@
475524
* [Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/undirected/clone_graph/node.py)
476525
* [Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/vertex.py)
477526
* Hashmap
527+
* [Bucket](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashmap/bucket.py)
528+
* [Hash Map](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashmap/hash_map.py)
529+
* [Item](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashmap/item.py)
478530
* [Test Hashmap](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashmap/test_hashmap.py)
479531
* Hashset
480532
* [Test My Hashset](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashset/test_my_hashset.py)
@@ -844,50 +896,6 @@
844896
* Queue
845897
* Recent Counter
846898
* [Test Recent Calls](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/queue/recent_counter/test_recent_calls.py)
847-
* Search
848-
* Binary Search
849-
* Big Words
850-
* [Big Words](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/big_words/big_words.py)
851-
* Cyclically Shifted Array
852-
* [Test Cyclically Shifted Array](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/cyclically_shifted_array/test_cyclically_shifted_array.py)
853-
* Find Closest Number
854-
* [Test Find Closest Number](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_closest_number/test_find_closest_number.py)
855-
* Find Closest Value
856-
* [Test Find Closest Value](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_closest_value/test_find_closest_value.py)
857-
* Find First In Duplicate List
858-
* [Test Find First In Duplicates](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_first_in_duplicate_list/test_find_first_in_duplicates.py)
859-
* Find Fixed Number
860-
* [Test Find Fixed Number](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_fixed_number/test_find_fixed_number.py)
861-
* Find Peak Element
862-
* [Test Find Peak Element](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/find_peak_element/test_find_peak_element.py)
863-
* First Boundary
864-
* [Test First Boundary](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/first_boundary/test_first_boundary.py)
865-
* First Occurrence
866-
* [Test Find First Occurrence](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/first_occurrence/test_find_first_occurrence.py)
867-
* Integer Square Root
868-
* [Test Integer Square Root](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/integer_square_root/test_integer_square_root.py)
869-
* Koko Eating Bananas
870-
* [Test Koko Eating Bananas](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/koko_eating_bananas/test_koko_eating_bananas.py)
871-
* Min In Rotated Sorted Array
872-
* [Test Find Min Rotated Sorted Array](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/min_in_rotated_sorted_array/test_find_min_rotated_sorted_array.py)
873-
* My Calendar
874-
* [Test My Calendar](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/my_calendar/test_my_calendar.py)
875-
* Next Greatest Letter
876-
* [Test Next Greatest Letter](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/next_greatest_letter/test_next_greatest_letter.py)
877-
* Peak Of Mountain
878-
* [Test Peak Of Mountain](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/peak_of_mountain/test_peak_of_mountain.py)
879-
* Plates Between Candles
880-
* [Test Plates Between Candles](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/plates_between_candles/test_plates_between_candles.py)
881-
* Rotated Sorted Array
882-
* [Test Search Rotated Sorted Array](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/rotated_sorted_array/test_search_rotated_sorted_array.py)
883-
* Search Range
884-
* [Test Search Range](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/search_range/test_search_range.py)
885-
* Single Non Duplicate
886-
* [Test Single Non Duplicate](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/single_non_duplicate/test_single_non_duplicate.py)
887-
* Square Root
888-
* [Test Sqrt Estimate](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/square_root/test_sqrt_estimate.py)
889-
* Successful Pairs Spells Potions
890-
* [Test Successful Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/search/binary_search/successful_pairs_spells_potions/test_successful_pairs.py)
891899
* Stack
892900
* Asteroid Collision
893901
* [Test Asteroid Collision](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/stack/asteroid_collision/test_asteroid_collision.py)
@@ -1100,7 +1108,6 @@
11001108
* [Test Variable Length Qty](https://github.com/BrianLusina/PythonSnips/blob/master/tests/algorithms/test_variable_length_qty.py)
11011109
* Cryptography
11021110
* [Test Atbash Cipher](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_atbash_cipher.py)
1103-
* [Test Caeser](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_caeser.py)
11041111
* [Test Crypto Square](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_crypto_square.py)
11051112
* [Test Diffie Hellman](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_diffie_hellman.py)
11061113
* [Test Handhsake](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_handhsake.py)
@@ -1111,7 +1118,6 @@
11111118
* [Test Pig Latin](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_pig_latin.py)
11121119
* [Test Rail Fence Cipher](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_rail_fence_cipher.py)
11131120
* [Test Rot13](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_rot13.py)
1114-
* [Test Run Length](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_run_length.py)
11151121
* [Test Shell Game](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_shell_game.py)
11161122
* [Test Simple Cipher](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_simple_cipher.py)
11171123
* [Test Vigenere](https://github.com/BrianLusina/PythonSnips/blob/master/tests/cryptography/test_vigenere.py)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Jewels and Stones
2+
3+
You're given strings `jewels` representing the types of stones that are jewels, and `stones` representing the stones you have.
4+
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.
5+
6+
Letters are case-sensitive, so "a" is considered a different type of stone from "A".
7+
8+
## Examples
9+
10+
Example 1:
11+
12+
```text
13+
Input: jewels = "aA", stones = "aAAbbbb"
14+
Output: 3
15+
```
16+
17+
Example 2:
18+
19+
```text
20+
Input: jewels = "z", stones = "ZZ"
21+
Output: 0
22+
```
23+
24+
## Constraints
25+
26+
- 1 <= jewels.length, stones.length <= 50
27+
- jewels and stones consist of only English letters.
28+
- All the characters of jewels are unique.
29+
30+
## Topics
31+
32+
- Hash Table
33+
- String
34+
35+
## Solution
36+
37+
The core intuition behind solving this problem is to treat it as a membership-counting task: we aren’t transforming
38+
either string, we’re simply counting how many characters in stones belong to the set of jewel types in jewels, while
39+
respecting case sensitivity. This maps naturally to a hash-based lookup because it lets us store all jewel types in a
40+
structure that supports fast membership checks. In other words, we treat jewels as an allowlist of valid types and stones
41+
as a stream of items to evaluate. As we scan through stones, we increment a counter whenever the current character appears
42+
in the jewel set. As comparisons are case-sensitive, only exact matches contribute to the final count, which represents
43+
how many of your stones are jewels.
44+
45+
Using the intuition above, we implement the algorithm as follows:
46+
47+
1. Initialize a new set, jewelSet, from the given jewels.
48+
2. Initialize a variable count to 0.
49+
3. Iterate through each character ch in the stones:
50+
- If ch exists in jewelSet:
51+
- Increment count.
52+
4. After successfully iterating through the stones array, return count.
53+
54+
### Time complexity
55+
56+
The time complexity of the solution is O(m+n) because it first builds a set from the m characters in jewels, then scans the
57+
n characters in stones once to count matches.
58+
59+
### Space complexity
60+
61+
The space complexity of the solution is O(m) because it stores up to m unique jewel characters in a set.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from collections import Counter
2+
from typing import Set
3+
4+
5+
def num_jewels_in_stones_with_set(jewels: str, stones: str) -> int:
6+
# Store all jewel types for fast membership checking
7+
jewel_set: Set[str] = set(jewels)
8+
9+
# Count how many stones are jewels
10+
count = 0
11+
12+
# Check each stone and increment count if it's a jewel
13+
for ch in stones:
14+
if ch in jewel_set:
15+
count += 1
16+
17+
# Return the total number of jewels found in stones
18+
return count
19+
20+
21+
def num_jewels_in_stones_with_dict(jewels: str, stones: str) -> int:
22+
# Store all jewel types for fast membership checking
23+
stone_counts: Counter[str] = Counter(stones)
24+
25+
# Count how many stones are jewels
26+
count = 0
27+
28+
# Check each stone and increment count if it's a jewel
29+
for jewel in jewels:
30+
if jewel in stone_counts:
31+
count += stone_counts[jewel]
32+
33+
# Return the total number of jewels found in stones
34+
return count
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import unittest
2+
from parameterized import parameterized
3+
from algorithms.hash_table.jewels_and_stones import (
4+
num_jewels_in_stones_with_dict,
5+
num_jewels_in_stones_with_set,
6+
)
7+
8+
JEWELS_AND_STONES_TEST_CASES = [
9+
("pQ", "ppPQQq", 4),
10+
("k", "kkkkK", 4),
11+
("LMn", "lLmMNn", 3),
12+
("cD", "ddddccccDD", 6),
13+
("tRz", "RttZzr", 4),
14+
]
15+
16+
17+
class JewelsAndStonesTestCase(unittest.TestCase):
18+
@parameterized.expand(JEWELS_AND_STONES_TEST_CASES)
19+
def test_num_jewels_in_stones_with_set(
20+
self, jewels: str, stones: str, expected: int
21+
):
22+
actual = num_jewels_in_stones_with_set(jewels, stones)
23+
self.assertEqual(actual, expected)
24+
25+
@parameterized.expand(JEWELS_AND_STONES_TEST_CASES)
26+
def test_num_jewels_in_stones_with_dict(
27+
self, jewels: str, stones: str, expected: int
28+
):
29+
actual = num_jewels_in_stones_with_dict(jewels, stones)
30+
self.assertEqual(actual, expected)
31+
32+
33+
if __name__ == "__main__":
34+
unittest.main()
File renamed without changes.

cryptography/caeser_cipher/__init__.py renamed to algorithms/strings/caeser_cipher/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def __init__(self, shift):
8282
self.alpha = ascii_uppercase
8383
self.new_alpha = self.alpha[shift:] + self.alpha[:shift]
8484

85-
def encode(self, plaintext):
85+
def encode(self, plaintext: str):
8686
t = plaintext.maketrans(self.alpha, self.new_alpha)
8787
return plaintext.upper().translate(t)
8888

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)