Skip to content

Commit

Permalink
Merge pull request #433 from lymchgmk/feat/week4
Browse files Browse the repository at this point in the history
[EGON] Week 4 Solutions
  • Loading branch information
SamTheKorean authored Sep 8, 2024
2 parents 33875e8 + fd4a944 commit 92359ca
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 0 deletions.
66 changes: 66 additions & 0 deletions longest-consecutive-sequence/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from typing import List
from unittest import TestCase, main


class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
return self.solveWithDict(nums)

"""
Runtime: 486 ms (Beats 40.61%)
Time Complexity:
- nums 배열 조회하며 연산에 O(n)
- 크기가 n인 node_dict.items()을 조회, visited에 의해 각 노드당 한 번만 방문,
- visited가 set이므로 갱신 및 확인에 O(1)
- 2개 항에 대해 max 연산하므로 O(2)로, 총 O(n)
> O(n) + O(n) ~= O(n)
Memory: 44.62 MB (Beats 5.00%)
Space Complexity: O(n)
- value가 크기 2짜리 배열이고 key가 최대 n인 dict 변수 사용에 O(2n)
- 최대 크기가 n인 visited 사용에 O(n)
> O(2n) + O(n) ~= O(n)
"""
def solveWithDict(self, nums: List[int]) -> int:
node_dict = {}
for num in nums:
curr_node = [num, num]
if num - 1 in node_dict:
prev_node = node_dict[num - 1]
curr_node[0] = prev_node[0]
prev_node[1] = curr_node[1]
if num + 1 in node_dict:
post_node = node_dict[num + 1]
curr_node[1] = post_node[1]
post_node[0] = curr_node[0]
node_dict[num] = curr_node

max_length = 0
visited = set()
for key, (prev_key, post_key) in node_dict.items():
while prev_key not in visited and prev_key in node_dict:
visited.add(prev_key)
prev_key = node_dict[prev_key][0]
while post_key not in visited and post_key in node_dict:
visited.add(post_key)
post_key = node_dict[post_key][1]
curr_length = post_key - prev_key + 1
max_length = max(max_length, curr_length)

return max_length


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [100, 4, 200, 1, 3, 2]
output = 4
self.assertEqual(Solution.longestConsecutive(Solution(), nums), output)

def test_2(self):
nums = [0, 3, 7, 2, 5, 8, 4, 6, 0, 1]
output = 9
self.assertEqual(Solution.longestConsecutive(Solution(), nums), output)


if __name__ == '__main__':
main()
67 changes: 67 additions & 0 deletions maximum-subarray/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import List
from unittest import TestCase, main


class Solution:
def maxProduct(self, nums: List[int]) -> int:
return self.solveWithDP(nums)

"""
Runtime: 71 ms (Beats 61.13%)
Time Complexity: O(n)
- dp 배열 초기화를 위한 nums.copy()에 O(n)
- range(1, L) 조회하며 조건에 따라 연산에 O(n - 1)
- range(L) 조회하며 max 계산에 O(n)
> O(n) + O(n - 1) + O(n) ~= O(n)
Memory: 17.75 MB (Beats 11.09%)
Space Complexity: O(n)
- 크기가 n인 배열 2개 사용했으므로 2 * O(n)
> O(2n) ~= O(n)
"""
def solveWithDP(self, nums: List[int]) -> int:
L = len(nums)
forward_product, backward_product = nums.copy(), nums.copy()
for i in range(1, L):
if forward_product[i - 1] != 0:
forward_product[i] *= forward_product[i - 1]

if backward_product[L - i] != 0:
backward_product[L - i - 1] *= backward_product[L - i]

result = nums[0]
for i in range(L):
result = max(result, forward_product[i], backward_product[i])

return result


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [2,3,-2,4]
output = 6
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_2(self):
nums = [-2,0,-1]
output = 0
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_3(self):
nums = [-2]
output = -2
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_4(self):
nums = [0,-3,-2,-3,-2,2,-3,0,1,-1]
output = 72
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_5(self):
nums = [7, -2, -4]
output = 56
self.assertEqual(Solution.maxProduct(Solution(), nums), output)


if __name__ == '__main__':
main()
48 changes: 48 additions & 0 deletions missing-number/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from typing import List
from unittest import TestCase, main


class Solution:
def missingNumber(self, nums: List[int]) -> int:
return self.solveWithSet(nums)

"""
Runtime: 118 ms (Beats 31.19%)
Time Complexity:
- 크기가 n + 1인 List를 set로 변환에 O(n + 1)
- nums 배열 조회하며 set.remove에 O(n) * O(1) ~= O(n)
- 마지막 set에서 pop하는데 O(1)
> O(n + 1) + O(n) + O(1) ~= O(n)
Memory: 18.56 MB (Beats 5.%)
Space Complexity:
- 크기가 n + 1인 set 사용에 O(n + 1)
> O(n + 1) ~= O(n)
"""
def solveWithSet(self, nums: List[int]) -> int:
range_set = set(range(0, len(nums) + 1))
for num in nums:
range_set.remove(num)

return range_set.pop()


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [3, 0, 1]
output = 2
self.assertEqual(Solution.missingNumber(Solution(), nums), output)

def test_2(self):
nums = [0, 1]
output = 2
self.assertEqual(Solution.missingNumber(Solution(), nums), output)

def test_3(self):
nums = [9, 6, 4, 2, 3, 5, 7, 0, 1]
output = 8
self.assertEqual(Solution.missingNumber(Solution(), nums), output)


if __name__ == '__main__':
main()
52 changes: 52 additions & 0 deletions valid-palindrome/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from unittest import TestCase, main


class Solution:
def isPalindrome(self, s: str) -> bool:
return self.solveWithTwoPointer(s)

"""
Runtime: 43 ms (Beats 68.60%)
Time Complexity: O(n)
- s 문자열 iterable 조회하며 연산에 O(n)
- range(0, length // 2) 조회에 O(n // 2)
> O(n) + O(n // 2) ~= O(n)
Memory: 17.02 MB (Beats 54.30%)
Space Complexity: O(n)
- 최대 크기가 n인 trimmed_s 변수 할당에 O(n)
> O(n)
"""
def solveWithPointer(self, s: str) -> bool:
trimmed_s = ""
for char in s:
if char.isalpha() or char.isnumeric():
trimmed_s += char.lower()

length = len(trimmed_s)
for i in range(0, length // 2):
if trimmed_s[i] != trimmed_s[length - i - 1]:
return False
else:
return True


class _LeetCodeTestCases(TestCase):
def test_1(self):
s = "A man, a plan, a canal: Panama"
output = True
self.assertEqual(Solution.isPalindrome(Solution(), s), output)

def test_2(self):
s = "race a car"
output = False
self.assertEqual(Solution.isPalindrome(Solution(), s), output)

def test_3(self):
s = " "
output = True
self.assertEqual(Solution.isPalindrome(Solution(), s), output)


if __name__ == '__main__':
main()
80 changes: 80 additions & 0 deletions word-search/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from typing import List
from unittest import TestCase, main


class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
return self.solveWithDFS(board, word)

"""
Runtime: 5005 ms (Beats 27.48%)
Time Complexity: O((MAX_R ** 2) * (MAX_C ** 2)), upper bound
- 이중 for문 조회에 O(MAX_R * MAX_C)
- node 하나당 조회하는 DIRS의 크기가 4이고, 최대 word의 길이만큼 반복하므로 O(4 * L)
- 단 early return하므로 이는 upper bound
> O(MAX_R * MAX_C) * O(4L) ~= O(MAX_R * MAX_C * L)
Memory: 16.59 MB (Beats 69.71%)
Space Complexity:
- MAX_R * MAX_C 격자의 칸마다 stack이 생성될 수 있으므로 O(MAX_R * MAX_C)
- node의 크기는 visited에 지배적이고(curr_word 무시 가정), visited의 크기는 최대 L
> O(MAX_R * MAX_C) * O(L) ~= O(MAX_R * MAX_C * L)
"""
def solveWithDFS(self, board: List[List[str]], word: str) -> bool:
MAX_R, MAX_C, MAX_IDX = len(board), len(board[0]), len(word)
DIRS = ((-1, 0), (1, 0), (0, -1), (0, 1))

for r in range(MAX_R):
for c in range(MAX_C):
if board[r][c] == word[0]:
stack = [(r, c, 0, board[r][c], set([(r, c)]))]
while stack:
curr_r, curr_c, curr_idx, curr_word, curr_visited = stack.pop()

if curr_word == word:
return True

for dir_r, dir_c in DIRS:
post_r, post_c, post_idx = curr_r + dir_r, curr_c + dir_c, curr_idx + 1
if (post_r, post_c) in curr_visited:
continue
if not (0 <= post_r < MAX_R and 0 <= post_c < MAX_C):
continue
if not (post_idx < MAX_IDX and board[post_r][post_c] == word[post_idx]):
continue

post_visited = curr_visited.copy()
post_visited.add((post_r, post_c))
stack.append((post_r, post_c, post_idx, curr_word + word[post_idx], post_visited))

return False


class _LeetCodeTestCases(TestCase):
def test_1(self):
board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]]
word = "ABCCED"
output = True
self.assertEqual(Solution.exist(Solution(), board, word), output)

def test_2(self):
board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]]
word = "SEE"
output = True
self.assertEqual(Solution.exist(Solution(), board, word), output)

def test_3(self):
board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]]
word = "ABCB"
output = False
self.assertEqual(Solution.exist(Solution(), board, word), output)

def test_4(self):
board = [["A","B","C","E"],["S","F","E","S"],["A","D","E","E"]]
word = "ABCESEEEFS"
output = True
self.assertEqual(Solution.exist(Solution(), board, word), output)


if __name__ == '__main__':
main()

0 comments on commit 92359ca

Please sign in to comment.