Skip to content

Commit 017e586

Browse files
authored
Merge pull request #1462 from ivan1016017/november21
adding algo
2 parents b866f19 + b4780b0 commit 017e586

6 files changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
from abc import ABC, abstractmethod
3+
4+
class Solution:
5+
def twoSum(self, nums: List[int], target: int) -> List[int]:
6+
7+
answer = dict()
8+
9+
for k, v in enumerate(nums):
10+
11+
if v in answer:
12+
return [answer[v], k]
13+
else:
14+
answer[target - v] = k
15+
16+
return []
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
from abc import ABC, abstractmethod
3+
import re
4+
5+
class Solution:
6+
def isPalindrome(self, s: str) -> bool:
7+
8+
# To lowercase
9+
s = s.lower()
10+
11+
# Remove non-alphanumeric characters
12+
s = re.sub(pattern=r'[^a-zA-Z0-9]', repl='', string=s)
13+
14+
# Determine if s is palindrome or not
15+
16+
len_s = len(s)
17+
18+
for i in range(len_s//2):
19+
20+
if s[i] != s[len_s - 1 - i]:
21+
return False
22+
23+
return True
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
from collections import defaultdict
3+
4+
class Solution:
5+
def countTheNumOfKFreeSubsets(self, nums: List[int], k: int) -> int:
6+
"""
7+
Count k-Free subsets using dynamic programming.
8+
9+
Approach:
10+
1. Group elements by (num % k) to find independent groups
11+
2. Within each group, sort and build chains where elements differ by k
12+
3. For each chain, use House Robber DP to count valid subsets
13+
4. Multiply results across all independent chains
14+
15+
Time: O(n log n), Space: O(n)
16+
"""
17+
# Group numbers by their remainder when divided by k
18+
groups = defaultdict(list)
19+
for num in nums:
20+
groups[num % k].append(num)
21+
22+
res = 1
23+
24+
# Process each group independently
25+
for group in groups.values():
26+
group.sort()
27+
28+
# Build chains within this group
29+
i = 0
30+
while i < len(group):
31+
chain = [group[i]]
32+
j = i + 1
33+
34+
# Build chain where each element is exactly k more than previous
35+
while j < len(group) and group[j] == chain[-1] + k:
36+
chain.append(group[j])
37+
j += 1
38+
39+
# House Robber DP for this chain
40+
m = len(chain)
41+
if m == 1:
42+
chain_res = 2 # {} or {chain[0]}
43+
else:
44+
take = 1 # Take first element
45+
skip = 1 # Skip first element
46+
47+
for idx in range(1, m):
48+
new_take = skip # Can only take current if we skipped previous
49+
new_skip = take + skip # Can skip current regardless
50+
take, skip = new_take, new_skip
51+
52+
chain_res = take + skip
53+
54+
res *= chain_res
55+
i = j
56+
57+
return res
58+
59+
60+
61+
62+
63+
'''
64+
Detailed Algorithm Explanation
65+
Part 1: Why Group by num % k?
66+
Two numbers can have a difference of exactly k only if they have the same remainder when divided by k.
67+
68+
Mathematical proof:
69+
70+
If a - b = k, then a = b + k
71+
Therefore: a % k = (b + k) % k = b % k
72+
Example: nums = [2, 3, 5, 8], k = 5
73+
74+
num | num % 5 | group
75+
----|---------|-------
76+
2 | 2 | Group A
77+
3 | 3 | Group B
78+
5 | 0 | Group C
79+
8 | 3 | Group B
80+
81+
82+
Why this matters: Elements from different groups can never differ by k, so they're independent. We can combine any subset from Group A with any subset from Group B.
83+
84+
Part 2: Building Chains
85+
Within each group, we sort and find chains where consecutive elements differ by exactly k.
86+
87+
Example with Group B: [3, 8]
88+
89+
Sorted: [3, 8]
90+
Check: 8 - 3 = 5 ✓
91+
Chain: 3 → 8
92+
93+
Another example: nums = [1, 6, 11, 21], k = 5 (all have remainder 1)
94+
95+
Sorted: [1, 6, 11, 21]
96+
Check: 6-1=5 ✓, 11-6=5 ✓, 21-11=10 ✗
97+
Chains: [1 → 6 → 11], [21]
98+
99+
100+
Part 3: House Robber DP - The Core Logic
101+
For a chain like [3 → 8], we can't pick both 3 and 8 (they differ by k). This is the House Robber problem: count all subsets where we don't pick adjacent elements.
102+
103+
DP State Variables
104+
take = number of valid subsets that INCLUDE the current element
105+
skip = number of valid subsets that EXCLUDE the current element
106+
107+
DP Transitions
108+
new_take = skip # To take current, we MUST have skipped previous
109+
new_skip = take + skip # To skip current, we can take or skip previous
110+
111+
'''
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
from abc import ABC, abstractmethod
3+
4+
class Solution:
5+
def kidsWithCandies(self, candies: List[int], extraCandies: int) -> List[bool]:
6+
max_candies = max(candies)
7+
return [candy + extraCandies >= max_candies for candy in candies]
8+
9+
10+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
from abc import ABC, abstractmethod
3+
4+
class Solution:
5+
def hammingWeight(self, n: int) -> int:
6+
return bin(n).count('1')
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import unittest
2+
from src.my_project.interviews.top_150_questions_round_21\
3+
.number_of_1_bits import Solution
4+
5+
class NumberOfOnesTestCase(unittest.TestCase):
6+
7+
def test_number_of_ones(self):
8+
solution = Solution()
9+
output = solution.hammingWeight(n=11)
10+
target = 3
11+
self.assertEqual(output, target)

0 commit comments

Comments
 (0)