Skip to content

Commit 2dece8c

Browse files
authored
Merge pull request #1533 from ivanpenaloza/january31
adding algo
2 parents 35532f6 + c8a93af commit 2dece8c

5 files changed

Lines changed: 244 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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import List
2+
3+
class Solution:
4+
def jump(self, nums: List[int]) -> int:
5+
"""
6+
Greedy approach: At each position, jump to the farthest reachable index
7+
8+
Example: [2,3,1,1,4]
9+
- From index 0 (value=2): can reach indices 1,2
10+
- Greedy choice: Jump to index 1 (value=3) because it reaches farthest
11+
- From index 1: can reach indices 2,3,4 (end)
12+
- Answer: 2 jumps
13+
"""
14+
15+
if len(nums) <= 1:
16+
return 0
17+
18+
jumps = 0
19+
current_end = 0 # End of current jump range
20+
farthest = 0 # The farthest position we can reach
21+
22+
for i in range(len(nums) - 1):
23+
# Update farthest position reachable
24+
farthest = max(farthest, i + nums[i])
25+
26+
# If we've reached the end of current jump range
27+
if i == current_end:
28+
jumps += 1
29+
current_end = farthest # Make the greedy choice
30+
31+
if current_end > len(nums) - 1:
32+
break
33+
return jumps
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from typing import List, Union, Collection, Mapping, Optional
2+
3+
4+
class Solution:
5+
def minCost(self, nums: List[int], costs: List[int]) -> int:
6+
"""
7+
Example: nums = [3, 1, 2, 4], costs = [1, 2, 3, 4]
8+
9+
From index 0 (value=3):
10+
- Next smaller: index 1 (value=1)
11+
- Next larger: index 3 (value=4)
12+
13+
DP builds cost backwards:
14+
- dp[3] = 0 (at end, no cost)
15+
- dp[2] = dp[3] + costs[3] = 0 + 4 = 4 (can jump to 4)
16+
- dp[1] = dp[2] + costs[2] = 4 + 3 = 7 (can jump to 2)
17+
- dp[0] = min(dp[1]+costs[1], dp[3]+costs[3]) = min(7+2, 0+4) = 4
18+
"""
19+
20+
smallStack = [] # Monotonic increasing (next smaller element)
21+
largeStack = [] # Monotonic decreasing (next larger element)
22+
dp = [0] * len(nums) # dp[i] = min cost from i to end
23+
24+
# Process backwards (right to left)
25+
for i in range(len(nums) - 1, -1, -1):
26+
27+
# Maintain monotonic increasing stack (for next smaller)
28+
# Remove elements >= current (they can't be "next smaller")
29+
while smallStack and nums[smallStack[-1]] >= nums[i]:
30+
smallStack.pop()
31+
32+
# Maintain monotonic decreasing stack (for next larger)
33+
# Remove elements < current (they can't be "next larger")
34+
while largeStack and nums[largeStack[-1]] < nums[i]:
35+
largeStack.pop()
36+
37+
# Calculate minimum cost for this position
38+
nxtCost = []
39+
40+
if largeStack:
41+
lid = largeStack[-1] # Next larger index
42+
nxtCost.append(dp[lid] + costs[lid])
43+
44+
if smallStack:
45+
sid = smallStack[-1] # Next smaller index
46+
nxtCost.append(dp[sid] + costs[sid])
47+
48+
# Min cost from current position to end
49+
dp[i] = min(nxtCost) if nxtCost else 0
50+
51+
# Add current index to both stacks
52+
largeStack.append(i)
53+
smallStack.append(i)
54+
55+
print(smallStack)
56+
print(largeStack)
57+
58+
59+
print(dp)
60+
61+
return dp[0] # Minimum cost from start
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+
'''

0 commit comments

Comments
 (0)