Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions best-time-to-buy-and-sell-stock/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'''
# 121. Best Time to Buy and Sell Stock

use **bottom-up dynamic programming** to solve the problem.

## Time and Space Complexity
```
TC: O(n)
SC: O(1)
```

## TC is O(n):
- iterating through the list just once to find the maximum profit. = O(n)

## SC is O(1):
- using two variables to store the minimum price and maximum profit. = O(1)
'''
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) == 1:
return 0

lowest_price = prices[0] # SC: O(1)
max_profit = 0 # SC: O(1)

for price in prices: # TC: O(n)
lowest_price = min(price, lowest_price)
curr_profit = price - lowest_price
max_profit = max(curr_profit, max_profit)

return max_profit
65 changes: 65 additions & 0 deletions encode-and-decode-strings/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'''
# 271. Encode and Decode Strings

## Time and Space Complexity

Use ":" as a separator and also store the length of each string to decode the string correctly.


### encode

```
TC: O(n * k)
SC: O(m)
```

#### TC is O(n * k):

- iterating through the list of strings and appending each string to the result. = O(n * k)
- f-string is O(k)

#### SC is O(m):
- storing the result in a string.

### decode

```
TC: O(m)
SC: O(m)
```

#### TC is O(m):
- iterating over the string until the string length is 0. = O(m)
- do list slicings for extract parts and removing the processed section = each operation takes O(k)

#### SC is O(m):
- storing the result in a list(total length of strings is m) = O(m)

'''

class Solution:
"""
@param: strs: a list of strings
@return: encodes a list of strings to a single string.
"""
def encode(self, strs):
result = ''
for str in strs: # TC: O(n)
result += f"{len(str)}:{str}" # TC: O(k)
return result

"""
@param: str: A string
@return: decodes a single string to a list of strings
"""
def decode(self, str):
result = []

while len(str) > 0: # TC: O(m)
length = int(str[:1]) # TC: O(k)
string = str[2:length+2] # TC: O(k)
str = str[length+2:] # TC: O(k)

result.append(string) # SC: O(m)

return result
27 changes: 27 additions & 0 deletions group-anagrams/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'''
# 49. Group Anagrams

use **hash map** to solve the problem.

## Time and Space Complexity
```
TC: O(N * K * Log(K))
SC: O(N * K)
```

## TC is O(N * K * Log(K)):
- iterating through the list of strings and sorting each string. = O(N * K * Log(K))

## SC is O(N * K):
- using a hash map to store the sorted strings. = O(N * K)
'''
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
result = defaultdict(list)

for str in strs: # TC: O(N)
sorted_key = ''.join(sorted(str)) # sorting 👉 TC: O(K * Log(K))
result[sorted_key].append(str) # TC: O(1) on average

return list(result.values())

49 changes: 49 additions & 0 deletions implement-trie-prefix-tree/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'''
# 208. Implement Trie (Prefix Tree)

```
- Node structure
{children: {char: {children: { ... }, is_end: False}}, is_end: False}
```

## Time and Space Complexity

```
TC: O(n)
SC: O(n)
```

#### TC is O(n):
- insert, search, startsWith: iterating through the word just once. = O(n)

#### SC is O(n):
- insert, search, startsWith: using a hash map to store the children nodes. = O(n)
'''

class Trie:
def __init__(self):
self.root = {"children": {}, "is_end": False}

def insert(self, word: str) -> None:
node = self.root
for char in word:
if char not in node["children"]:
node["children"][char] = {"children": {}, "is_end": False}
node = node["children"][char]
node["is_end"] = True

def search(self, word: str) -> bool:
node = self.root
for char in word:
if char not in node["children"]:
return False
node = node["children"][char]
return node["is_end"]

def startsWith(self, prefix: str) -> bool:
node = self.root
for char in prefix:
if char not in node["children"]:
return False
node = node["children"][char]
return True
38 changes: 38 additions & 0 deletions word-break/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'''
# 139. Word Break

use dynamic programming to check if the string is segmentable.

> **use dp to avoid redundant computations:**
> backtracking approach will check all possible combinations of words recursively, which has time complexity of O(2^n))

## Time and Space Complexity

```
TC: O(n^2)
SC: O(n)
```

#### TC is O(n^2):
- nested loops for checking if the string is segmentable. = O(n^2)
- outer loop: iterate each char index from the start to the end. = O(n)
- inner loop: for each index in the outer loop, checks substrings within the range of valid words in wordDict. = worst case, O(n)

#### SC is O(n):
- using a dp array to store whether index i is segmentable. = O(n)
'''
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
word_set = set(wordDict)
n = len(s)

segment_dp = [False] * (n + 1) # SC: O(n)
segment_dp[0] = True # Base case: an empty string is segmentable

for end in range(1, n + 1): # TC: O(n^2)
for start in range(end):
if segment_dp[start] and s[start:end] in word_set:
segment_dp[end] = True
break

return segment_dp[n]
Loading