Skip to content

[seungriyou] Week 06 Solutions #1421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 7, 2025
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
38 changes: 38 additions & 0 deletions container-with-most-water/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# https://leetcode.com/problems/container-with-most-water/

from typing import List

class Solution:
def maxArea(self, height: List[int]) -> int:
"""
[Complexity]
- TC: O(n)
- SC: O(1)

[Approach]
양끝에서부터 범위를 좁히면서 확인하는 two-pointer를 이용한다.
양끝 side 중 더 작은 쪽을 안쪽으로 한 칸 옮기면서 확인하고,
만약 두 side가 길이가 같다면 두 개를 모두 안쪽으로 한 칸 옮긴다.
"""

lo, hi = 0, len(height) - 1
max_amount = 0

while lo < hi:
# 양 끝 side 길이
s1, s2 = height[lo], height[hi]

# max_amount 업데이트
max_amount = max(max_amount, (hi - lo) * min(s1, s2))

# 더 낮은 side를 안쪽으로 한 칸 옮기기
if s1 < s2:
lo += 1
elif s1 > s2:
hi -= 1
# 두 side의 크기가 같다면, 둘다 안쪽으로 한 칸씩 옮기기
else:
lo += 1
hi -= 1

return max_amount
79 changes: 79 additions & 0 deletions design-add-and-search-words-data-structure/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# https://leetcode.com/problems/design-add-and-search-words-data-structure/

from collections import defaultdict, deque

class TrieNode:
def __init__(self):
self.is_word = False
self.children = defaultdict(TrieNode)

class WordDictionary:
def __init__(self):
self.root = TrieNode()

def addWord(self, word: str) -> None:
curr = self.root

for w in word:
curr = curr.children[w]

curr.is_word = True

def search(self, word: str) -> bool:
"""
BFS 혹은 DFS를 사용할 수 있다. 핵심은
- "."을 마주치면 모든 children을 전부 타고 내려간다.
- "."이 아닌 문자를 확인해야 한다면 해당 문자가 children에 있는지 확인 후 타고 내려간다.
는 것이다.
"""
# return self._bfs(word)
return self._dfs(self.root, word, 0)

def _dfs(self, curr, word, idx):
# base condition
if idx == len(word):
return curr.is_word

# recur
# (1) "."을 마주치면 모든 children에 대해 타고 내려간다.
if word[idx] == ".":
for child in curr.children.values():
if self._dfs(child, word, idx + 1):
return True

# (2) 현재 idx의 문자가 children에 있는지 확인 후에 타고 내려간다.
if (w := word[idx]) in curr.children:
return self._dfs(curr.children[w], word, idx + 1)

# (3) 그 외의 경우, False를 반환한다.
return False

def _bfs(self, word):
q = deque([(self.root, 0)]) # (node, idx)

while q:
curr, idx = q.popleft()

# base condition
if idx == len(word):
if curr.is_word:
return True

# iter
# (1) "."을 마주치면 모든 children에 대해 타고 내려간다.
elif word[idx] == ".":
for child in curr.children.values():
q.append((child, idx + 1))

# (2) 현재 idx의 문자가 children에 있는지 확인 후에 타고 내려간다.
else:
if (w := word[idx]) in curr.children:
q.append((curr.children[w], idx + 1))

# (3) 그 외의 경우, False를 반환한다.
return False

# Your WordDictionary object will be instantiated and called as such:
# obj = WordDictionary()
# obj.addWord(word)
# param_2 = obj.search(word)
62 changes: 62 additions & 0 deletions longest-increasing-subsequence/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# https://leetcode.com/problems/longest-increasing-subsequence/

from typing import List

class Solution:
def lengthOfLIS_dp(self, nums: List[int]) -> int:
"""
[Complexity]
- TC: O(n^2)
- SC: O(n)

[Approach]
dp[i] = nums[i]가 포함되는 LIS의 최대 길이
"""
n = len(nums)
dp = [1] * n

for i in range(n):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)

return max(dp)

def lengthOfLIS(self, nums: List[int]) -> int:
"""
[Complexity]
- TC: O(nlogn) (w. binary search)
- SC: O(n)

[Approach]
ref: https://leetcode.com/problems/longest-increasing-subsequence/solutions/1326308/c-python-dp-binary-search-bit-segment-tree-solutions-picture-explain-o-nlogn
nums를 순차적으로 순회하며 LIS를 모은다. 이때,
- 현재 보고 있는 원소 n이 LIS의 마지막 원소 이하라면: LIS의 원소 중 (1) n 이상이면서 (2) 최솟값의 위치에 n 덮어쓰기
- 그 외의 경우라면: LIS의 맨 끝에 num append
한다.
LIS는 정렬되어 있을 것이므로 binary search를 이용할 수 있으며, 이렇게 구성한 LIS의 길이가 최대 길이이다.
"""

def find_leftmost_idx(lis, n):
lo, hi = 0, len(lis) - 1

while lo < hi:
mid = lo + (hi - lo) // 2
if lis[mid] < n: # -- 아예 제외하는 경우: n 미만인 경우
lo = mid + 1
else:
hi = mid

return lo

lis = []

for n in nums:
# 현재 보고 있는 n이 LIS의 마지막 원소 이하라면
if lis and n <= lis[-1]:
lis[find_leftmost_idx(lis, n)] = n
# 그 외의 경우라면
else:
lis.append(n)

return len(lis)
41 changes: 41 additions & 0 deletions spiral-matrix/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# https://leetcode.com/problems/spiral-matrix/

from typing import List

class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
"""
[Complexity]
- TC: O(m * n) (m = row, n = col)
- SC: O(1) (결과용 배열 res 제외)

[Approach]
시계 방향으로 순회하도록, 방문 표시 & 방향 전환을 하면서 res에 값을 저장한다.
이때, 방문 표시를 inplace로 한다면 추가 공간을 사용하지 않을 수 있다.
"""

# 방향: 우 -> 하 -> 좌 -> 상
dr, dc = [0, 1, 0, -1], [1, 0, -1, 0]
row, col = len(matrix), len(matrix[0])

r, c, d = 0, 0, 0
res = [matrix[r][c]]
matrix[r][c] = "."

while len(res) < row * col:
# 다음 위치 후보 구하기
nr, nc = r + dr[d], c + dc[d]

# 다음 위치 후보가 (1) 범위를 벗어났거나 (2) 이미 방문한 위치라면, 방향 틀기
if not (0 <= nr < row and 0 <= nc < col) or matrix[nr][nc] == ".":
d = (d + 1) % 4
nr, nc = r + dr[d], c + dc[d]

# res에 추가 & 방문 표시
res.append(matrix[nr][nc])
matrix[nr][nc] = "."

# 이동
r, c = nr, nc

return res
35 changes: 35 additions & 0 deletions valid-parentheses/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# https://leetcode.com/problems/valid-parentheses/

class Solution:
def isValid(self, s: str) -> bool:
"""
[Complexity]
- TC: O(n)
- SC: O(n)

[Approach]
s를 순회하면서 다음을 stack에 수행한다.
- 닫는 괄호: stack이 비어있거나, stack.top이 매칭이 안 된다면 빠르게 False 반환 (+ 매칭이 된다면 stack.pop())
- 여는 괄호: stack에 추가
이렇게 전체를 순회했을 때 stack이 비어있어야 valid parentheses이다.
"""

matches = {
")": "(",
"}": "{",
"]": "["
}

stack = []

for p in s:
# 닫는 괄호: (1) stack이 비어있거나 (2) stack의 top이 pair가 아니라면 False (+ pair라면 stack.pop())
if p in matches:
if not stack or stack.pop() != matches[p]:
return False
# 여는 괄호: stack에 추가
else:
stack.append(p)

# stack이 비어있어야 valid parentheses
return not stack