Skip to content

Commit 6b1dc24

Browse files
committed
feat: add partition_equal_subset_sum
1 parent 57b6cd7 commit 6b1dc24

File tree

8 files changed

+500
-173
lines changed

8 files changed

+500
-173
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"problem_name": "partition_equal_subset_sum",
3+
"solution_class_name": "Solution",
4+
"problem_number": "416",
5+
"problem_title": "Partition Equal Subset Sum",
6+
"difficulty": "Medium",
7+
"topics": "Array, Dynamic Programming",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given an integer array `nums`, return `true` if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or `false` otherwise.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: nums = [1,5,11,5]\nOutput: true\n```\n**Explanation:** The array can be partitioned as [1, 5, 5] and [11]."
13+
},
14+
{
15+
"content": "```\nInput: nums = [1,2,3,5]\nOutput: false\n```\n**Explanation:** The array cannot be partitioned into equal sum subsets."
16+
}
17+
],
18+
"readme_constraints": "- 1 <= nums.length <= 200\n- 1 <= nums[i] <= 100",
19+
"readme_additional": "",
20+
"solution_imports": "",
21+
"solution_methods": [
22+
{
23+
"name": "can_partition",
24+
"parameters": "nums: list[int]",
25+
"return_type": "bool",
26+
"dummy_return": "False"
27+
}
28+
],
29+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
30+
"test_class_name": "PartitionEqualSubsetSum",
31+
"test_helper_methods": [
32+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
33+
],
34+
"test_methods": [
35+
{
36+
"name": "test_can_partition",
37+
"parametrize": "nums, expected",
38+
"parametrize_typed": "nums: list[int], expected: bool",
39+
"test_cases": "[([1, 5, 11, 5], True), ([1, 2, 3, 5], False), ([1, 1], True), ([1], False), ([2, 2, 1, 1], True)]",
40+
"body": "result = self.solution.can_partition(nums)\nassert result == expected"
41+
}
42+
],
43+
"playground_imports": "from solution import Solution",
44+
"playground_test_case": "# Example test case\nnums = [1, 5, 11, 5]\nexpected = True",
45+
"playground_execution": "result = Solution().can_partition(nums)\nresult",
46+
"playground_assertion": "assert result == expected"
47+
}

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 ?= word_break
2+
PROBLEM ?= partition_equal_subset_sum
33
FORCE ?= 0
44
COMMA := ,
55

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Partition Equal Subset Sum
2+
3+
**Difficulty:** Medium
4+
**Topics:** Array, Dynamic Programming
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 416](https://leetcode.com/problems/partition-equal-subset-sum/description/)
8+
9+
## Problem Description
10+
11+
Given an integer array `nums`, return `true` if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or `false` otherwise.
12+
13+
## Examples
14+
15+
### Example 1:
16+
17+
```
18+
Input: nums = [1,5,11,5]
19+
Output: true
20+
```
21+
22+
**Explanation:** The array can be partitioned as [1, 5, 5] and [11].
23+
24+
### Example 2:
25+
26+
```
27+
Input: nums = [1,2,3,5]
28+
Output: false
29+
```
30+
31+
**Explanation:** The array cannot be partitioned into equal sum subsets.
32+
33+
## Constraints
34+
35+
- 1 <= nums.length <= 200
36+
- 1 <= nums[i] <= 100

leetcode/partition_equal_subset_sum/__init__.py

Whitespace-only changes.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
"nums = [1, 5, 11, 5]\n",
22+
"expected = True"
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": 3,
28+
"id": "execute",
29+
"metadata": {},
30+
"outputs": [
31+
{
32+
"data": {
33+
"text/plain": [
34+
"True"
35+
]
36+
},
37+
"execution_count": 3,
38+
"metadata": {},
39+
"output_type": "execute_result"
40+
}
41+
],
42+
"source": [
43+
"result = Solution().can_partition(nums)\n",
44+
"result"
45+
]
46+
},
47+
{
48+
"cell_type": "code",
49+
"execution_count": 4,
50+
"id": "test",
51+
"metadata": {},
52+
"outputs": [],
53+
"source": [
54+
"assert result == expected"
55+
]
56+
}
57+
],
58+
"metadata": {
59+
"kernelspec": {
60+
"display_name": "leetcode-py-py3.13",
61+
"language": "python",
62+
"name": "python3"
63+
},
64+
"language_info": {
65+
"codemirror_mode": {
66+
"name": "ipython",
67+
"version": 3
68+
},
69+
"file_extension": ".py",
70+
"mimetype": "text/x-python",
71+
"name": "python",
72+
"nbconvert_exporter": "python",
73+
"pygments_lexer": "ipython3",
74+
"version": "3.13.7"
75+
}
76+
},
77+
"nbformat": 4,
78+
"nbformat_minor": 5
79+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
class Solution:
2+
# Time: O(n * sum)
3+
# Space: O(sum)
4+
def can_partition(self, nums: list[int]) -> bool:
5+
"""
6+
Example: nums = [1, 5, 11, 5], target = 11
7+
8+
Initial: dp = [T, F, F, F, F, F, F, F, F, F, F, F]
9+
0 1 2 3 4 5 6 7 8 9 10 11
10+
11+
After num=1: [T, T, F, F, F, F, F, F, F, F, F, F]
12+
└─┘ (can make sum 1)
13+
14+
After num=5: [T, T, F, F, F, T, T, F, F, F, F, F]
15+
└─┘ └─┘ └─┘ (can make sums 5,6)
16+
17+
After num=11:[T, T, F, F, F, T, T, F, F, F, F, T]
18+
└─┘ (target!)
19+
20+
Backward iteration prevents using same number twice
21+
"""
22+
total = sum(nums)
23+
if total % 2:
24+
return False
25+
26+
target = total // 2
27+
dp = [False] * (target + 1)
28+
dp[0] = True
29+
30+
for num in nums:
31+
for j in range(target, num - 1, -1):
32+
dp[j] = dp[j] or dp[j - num]
33+
34+
# Early termination: found target sum!
35+
if dp[target]:
36+
return True
37+
38+
return False
39+
40+
41+
class SolutionBitset:
42+
# Time: O(n * sum)
43+
# Space: O(1)
44+
def can_partition(self, nums: list[int]) -> bool:
45+
"""
46+
Example: nums = [1, 5, 11, 5], target = 11
47+
48+
Bitset representation (bit position = achievable sum):
49+
50+
Initial: dp = 1 (binary: 1)
51+
Bits: ...0001
52+
Sums: {0}
53+
54+
After num=1: dp |= dp << 1
55+
a = dp = 1 (bin: 0001)
56+
b = dp << 1 = 2 (bin: 0010)
57+
c = a | b = 3 (bin: 0011)
58+
Sums: {0, 1}
59+
60+
After num=5: dp |= dp << 5
61+
a = dp = 3 (bin: 0000011)
62+
b = dp << 5 = 96 (bin: 1100000)
63+
c = a | b = 99 (bin: 1100011)
64+
Sums: {0, 1, 5, 6}
65+
66+
After num=11: dp |= dp << 11
67+
a = dp = 99 (bin: 00000001100011)
68+
b = dp << 11 = 202752 (bin: 110001100000000)
69+
c = a | b = 202851 (bin: 110001101100011)
70+
Sums: {0, 1, 5, 6, 11, 12, 16, 17}
71+
72+
Check: (dp & (1 << 11)) != 0
73+
a = dp = 202851 (bin: 110001101100011)
74+
b = 1 << 11 = 2048 (bin: 100000000000)
75+
c = a & b = 2048 (bin: 100000000000)
76+
c != 0 → bit 11 is set → True!
77+
"""
78+
total = sum(nums)
79+
if total % 2 != 0:
80+
return False
81+
82+
target = total // 2
83+
dp = 1
84+
85+
for num in nums:
86+
dp |= dp << num
87+
88+
# Early termination: found target sum!
89+
if (dp & (1 << target)) != 0:
90+
return True
91+
92+
return False
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution, SolutionBitset
6+
7+
8+
class TestPartitionEqualSubsetSum:
9+
@pytest.mark.parametrize("solution_class", [Solution, SolutionBitset])
10+
@pytest.mark.parametrize(
11+
"nums, expected",
12+
[
13+
([1, 5, 11, 5], True),
14+
([1, 2, 3, 5], False),
15+
([1, 1], True),
16+
([1], False),
17+
([2, 2, 1, 1], True),
18+
# Edge cases
19+
([100], False),
20+
([1, 1, 1, 1], True),
21+
([2, 3, 5], True),
22+
# Large numbers
23+
([50, 50], True),
24+
([99, 1], False),
25+
([100, 100], True),
26+
# Multiple solutions
27+
([1, 2, 3, 4], True),
28+
([3, 3, 3, 4, 5], True),
29+
# No solution cases
30+
([1, 3, 5], False),
31+
([7, 11, 13], False),
32+
# Larger arrays
33+
([1, 1, 1, 1, 1, 1, 1, 1], True),
34+
([2, 4, 6, 8, 10], False),
35+
([5, 10, 15, 20], True),
36+
# Very large numbers (testing Python's arbitrary precision)
37+
([1000, 1000], True),
38+
([5000, 5000], True),
39+
([9999, 1], False),
40+
([10000, 10000], True),
41+
# Extremely large numbers (beyond 64-bit)
42+
([2**16, 2**16], True), # 65536 each, target = 65536
43+
([2**20, 2**20], True), # 1048576 each, target = 1048576
44+
([2**20 + 1, 2**20 - 1], False), # sum = 2^21, target = 2^20, cannot partition
45+
],
46+
)
47+
@logged_test
48+
def test_can_partition(
49+
self,
50+
nums: list[int],
51+
expected: bool,
52+
solution_class: type[Solution | SolutionBitset],
53+
):
54+
solution = solution_class()
55+
result = solution.can_partition(nums)
56+
assert result == expected

0 commit comments

Comments
 (0)