Skip to content

Commit 5b27fa9

Browse files
committed
feat: add Ransom Note
1 parent e586905 commit 5b27fa9

File tree

8 files changed

+223
-2
lines changed

8 files changed

+223
-2
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"problem_name": "ransom_note",
3+
"solution_class_name": "Solution",
4+
"problem_number": "383",
5+
"problem_title": "Ransom Note",
6+
"difficulty": "Easy",
7+
"topics": "Hash Table, String, Counting",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given two strings `ransomNote` and `magazine`, return `true` if `ransomNote` can be constructed by using the letters from `magazine` and `false` otherwise.\n\nEach letter in `magazine` can only be used once in `ransomNote`.",
10+
"readme_examples": [
11+
{ "content": "```\nInput: ransomNote = \"a\", magazine = \"b\"\nOutput: false\n```" },
12+
{ "content": "```\nInput: ransomNote = \"aa\", magazine = \"ab\"\nOutput: false\n```" },
13+
{ "content": "```\nInput: ransomNote = \"aa\", magazine = \"aab\"\nOutput: true\n```" }
14+
],
15+
"readme_constraints": "- 1 <= ransomNote.length, magazine.length <= 10^5\n- ransomNote and magazine consist of lowercase English letters.",
16+
"readme_additional": "",
17+
"solution_imports": "",
18+
"solution_methods": [
19+
{
20+
"name": "can_construct",
21+
"parameters": "ransom_note: str, magazine: str",
22+
"return_type": "bool",
23+
"dummy_return": "False"
24+
}
25+
],
26+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
27+
"test_class_name": "RansomNote",
28+
"test_helper_methods": [
29+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
30+
],
31+
"test_methods": [
32+
{
33+
"name": "test_can_construct",
34+
"parametrize": "ransom_note, magazine, expected",
35+
"parametrize_typed": "ransom_note: str, magazine: str, expected: bool",
36+
"test_cases": "[('a', 'b', False), ('aa', 'ab', False), ('aa', 'aab', True), ('aab', 'baa', True)]",
37+
"body": "result = self.solution.can_construct(ransom_note, magazine)\nassert result == expected"
38+
}
39+
],
40+
"playground_imports": "from solution import Solution",
41+
"playground_test_case": "# Example test case\nransom_note = 'aa'\nmagazine = 'aab'\nexpected = True",
42+
"playground_execution": "result = Solution().can_construct(ransom_note, magazine)\nresult",
43+
"playground_assertion": "assert result == expected"
44+
}

.templates/leetcode/{{cookiecutter.problem_name}}/solution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{cookiecutter.solution_imports}}
2-
2+
{# TODO: add helper class like class Node: .... #}
33
class {{cookiecutter.solution_class_name}}:
44
{%- for _, methods in cookiecutter._solution_methods | dictsort %}
55
{%- for method in methods %}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= merge_k_sorted_lists
2+
PROBLEM ?= ransom_note
33
FORCE ?= 0
44
COMMA := ,
55

leetcode/ransom_note/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Ransom Note
2+
3+
**Difficulty:** Easy
4+
**Topics:** Hash Table, String, Counting
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 383](https://leetcode.com/problems/ransom-note/description/)
8+
9+
## Problem Description
10+
11+
Given two strings `ransomNote` and `magazine`, return `true` if `ransomNote` can be constructed by using the letters from `magazine` and `false` otherwise.
12+
13+
Each letter in `magazine` can only be used once in `ransomNote`.
14+
15+
## Examples
16+
17+
### Example 1:
18+
19+
```
20+
Input: ransomNote = "a", magazine = "b"
21+
Output: false
22+
```
23+
24+
### Example 2:
25+
26+
```
27+
Input: ransomNote = "aa", magazine = "ab"
28+
Output: false
29+
```
30+
31+
### Example 3:
32+
33+
```
34+
Input: ransomNote = "aa", magazine = "aab"
35+
Output: true
36+
```
37+
38+
## Constraints
39+
40+
- 1 <= ransomNote.length, magazine.length <= 10^5
41+
- ransomNote and magazine consist of lowercase English letters.

leetcode/ransom_note/__init__.py

Whitespace-only changes.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "setup",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"# Example test case\n",
21+
"ransom_note = \"aa\"\n",
22+
"magazine = \"aab\"\n",
23+
"expected = True"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": 3,
29+
"id": "execute",
30+
"metadata": {},
31+
"outputs": [
32+
{
33+
"data": {
34+
"text/plain": [
35+
"True"
36+
]
37+
},
38+
"execution_count": 3,
39+
"metadata": {},
40+
"output_type": "execute_result"
41+
}
42+
],
43+
"source": [
44+
"result = Solution().can_construct(ransom_note, magazine)\n",
45+
"result"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": 4,
51+
"id": "test",
52+
"metadata": {},
53+
"outputs": [],
54+
"source": [
55+
"assert result == expected"
56+
]
57+
}
58+
],
59+
"metadata": {
60+
"kernelspec": {
61+
"display_name": "leetcode-py-py3.13",
62+
"language": "python",
63+
"name": "python3"
64+
},
65+
"language_info": {
66+
"codemirror_mode": {
67+
"name": "ipython",
68+
"version": 3
69+
},
70+
"file_extension": ".py",
71+
"mimetype": "text/x-python",
72+
"name": "python",
73+
"nbconvert_exporter": "python",
74+
"pygments_lexer": "ipython3",
75+
"version": "3.13.7"
76+
}
77+
},
78+
"nbformat": 4,
79+
"nbformat_minor": 5
80+
}

leetcode/ransom_note/solution.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from collections import Counter
2+
3+
4+
class Solution:
5+
# Time: O(m + n) where m = magazine length, n = ransom_note length
6+
# Space: O(1) - at most 26 lowercase letters
7+
def can_construct(self, ransom_note: str, magazine: str) -> bool:
8+
if len(ransom_note) > len(magazine):
9+
return False
10+
11+
magazine_count = Counter(magazine)
12+
13+
for char in ransom_note:
14+
if magazine_count[char] == 0:
15+
return False
16+
magazine_count[char] -= 1
17+
18+
return True

leetcode/ransom_note/tests.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution
6+
7+
8+
class TestRansomNote:
9+
def setup_method(self):
10+
self.solution = Solution()
11+
12+
@pytest.mark.parametrize(
13+
"ransom_note, magazine, expected",
14+
[
15+
# Original test cases
16+
("a", "b", False),
17+
("aa", "ab", False),
18+
("aa", "aab", True),
19+
("aab", "baa", True),
20+
# Edge cases
21+
("", "", True), # Both empty
22+
("", "abc", True), # Empty ransom note
23+
("a", "", False), # Empty magazine
24+
("a", "a", True), # Single char match
25+
("ab", "a", False), # Ransom longer than magazine
26+
# More complex cases
27+
("abc", "aabbcc", True), # Multiple of each char
28+
("aab", "baa", True), # Same chars, different order
29+
("aaa", "aa", False), # Not enough of same char
30+
("abcd", "dcba", True), # All different chars
31+
("hello", "helloworld", True), # Ransom is substring
32+
("world", "hello", False), # Missing chars
33+
],
34+
)
35+
@logged_test
36+
def test_can_construct(self, ransom_note: str, magazine: str, expected: bool):
37+
result = self.solution.can_construct(ransom_note, magazine)
38+
assert result == expected

0 commit comments

Comments
 (0)