Skip to content

Commit 8082147

Browse files
authored
Merge pull request #1664 from seungriyou/main
[seungriyou] Week 15 Solutions
2 parents 04ad910 + b883214 commit 8082147

File tree

5 files changed

+345
-0
lines changed

5 files changed

+345
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# https://leetcode.com/problems/alien-dictionary/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def alienOrder(self, words: List[str]) -> str:
7+
"""
8+
[Complexity]
9+
(V = words ๋‚ด unique ๋ฌธ์ž ๊ฐœ์ˆ˜, E = edge ๊ฐœ์ˆ˜, L = words๋‚ด ๋ชจ๋“  ๋ฌธ์ž ๊ฐœ์ˆ˜)
10+
- TC: O(V + L) (topo sort์— O(V + E) ์†Œ์š” -> E < L ์ด๋ฏ€๋กœ)
11+
- SC: O(V + E) (graph)
12+
13+
[Approach]
14+
topo sort๋ฅผ ์ด์šฉํ•˜์—ฌ, words์— ํฌํ•จ๋œ ์ „์ฒด ๋ฌธ์ž ๊ฐœ์ˆ˜์™€ topo sort ์ˆœ์œผ๋กœ ๋ฐฉ๋ฌธํ•œ ๋ฌธ์ž ๊ฐœ์ˆ˜๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
15+
1) directed graph๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ, words์—์„œ ๋‘ ์ธ์ ‘ํ•œ ๋‹จ์–ด๋ฅผ ๋น„๊ตํ•˜๋ฉฐ ์ฒซ ๋ฒˆ์งธ๋กœ ๋‹ค๋ฅธ ๋ฌธ์ž๊ฐ€ ๋‚˜์˜ฌ ๋•Œ graph & indegree๋ฅผ ๊ธฐ๋กํ•œ๋‹ค. (sorted lexicographically)
16+
์ด๋•Œ, ์ˆœ์„œ resolving์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ๋ฅผ ํŒ๋‹จํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
17+
(์ธ์ ‘ํ•œ ๋‘ ๋‹จ์–ด๋ฅผ ์ˆœ์„œ๋Œ€๋กœ w1, w2๋ผ๊ณ  ํ•  ๋•Œ, (1) w2๋ณด๋‹ค w1์ด ๋” ๊ธธ๋ฉด์„œ (2) w2๊ฐ€ w1์— ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ, w1์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์— ์žˆ๋Š” ๋ฌธ์ž๋Š” ์ˆœ์„œ๋ฅผ ์•Œ ์ˆ˜ ์—†๋‹ค.)
18+
2) ์ด๋ ‡๊ฒŒ ๊ธฐ๋กํ•œ graph & indegree๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ topo sort๋กœ ๋ฐฉ๋ฌธํ•œ ๋ฌธ์ž ๊ฐœ์ˆ˜์™€ ์ „์ฒด ๋ฌธ์ž ๊ฐœ์ˆ˜๋ฅผ ๋น„๊ตํ•˜์—ฌ cycle์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•˜๊ณ ,
19+
cycle์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜, cycle์ด ๋ฐœ์ƒํ–ˆ๋‹ค๋ฉด ๋นˆ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
20+
"""
21+
from collections import defaultdict, deque
22+
23+
# directed graph
24+
graph, indegree = {}, {}
25+
for word in words:
26+
for w in word:
27+
graph[w] = set()
28+
indegree[w] = 0
29+
30+
for i in range(len(words) - 1):
31+
w1, w2 = words[i], words[i + 1]
32+
# ๋‘ ์ธ์ ‘ํ•œ ๋‹จ์–ด๋ฅผ ๋น„๊ตํ•˜๋ฉด์„œ, ์ฒซ ๋ฒˆ์งธ๋กœ ๋‹ค๋ฅธ ๋ฌธ์ž๊ฐ€ ๋‚˜์˜ฌ ๋•Œ graph & indegree ๊ธฐ๋ก
33+
for j in range(min(len(w1), len(w2))):
34+
c1, c2 = w1[j], w2[j]
35+
# ์ฒซ ๋ฒˆ์งธ๋กœ ๋‹ค๋ฅธ ๋ฌธ์ž ๋ฐœ๊ฒฌ ์‹œ, ๊ธฐ๋ก ํ›„ ๋‹ค์Œ ๋‹จ์–ด๋กœ ๋„˜์–ด๊ฐ€๊ธฐ
36+
if c1 != c2:
37+
if c2 not in graph[c1]:
38+
graph[c1].add(c2)
39+
indegree[c2] += 1
40+
break
41+
# ์ˆœ์„œ๋ฅผ resolve ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, ๋น ๋ฅด๊ฒŒ ๋ฆฌํ„ด *****
42+
# ex) words = ["abc", "ab"] (w1 = "abc", w2 = "ab")
43+
# -> (1) w2๋ณด๋‹ค w1์ด ๋” ๊ธธ๊ณ  (2) w2๊ฐ€ w1์— ํฌํ•จ๋œ๋‹ค๋ฉด (=ํ˜„์žฌ j๊ฐ€ w2์˜ ๋งˆ์ง€๋ง‰ ๋ฌธ์ž๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋‹ค๋ฉด)
44+
# w1[j + 1] ์ดํ›„์˜ ๋ฌธ์ž์— ๋Œ€ํ•ด์„œ๋Š” ์ˆœ์„œ๋ฅผ resolve ํ•  ์ˆ˜ ์—†์Œ
45+
elif len(w1) > len(w2) and j == min(len(w1), len(w2)) - 1:
46+
return ""
47+
48+
# topo sort
49+
q = deque([w for w, ind in indegree.items() if ind == 0 and w in graph])
50+
res = []
51+
52+
while q:
53+
w = q.popleft()
54+
res.append(w)
55+
56+
for nw in graph[w]:
57+
indegree[nw] -= 1
58+
if indegree[nw] == 0:
59+
q.append(nw)
60+
61+
return "".join(res) if len(res) == len(graph) else ""
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
2+
3+
from typing import List, Optional
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
"""
13+
ํŠธ๋ฆฌ๋ฅผ ์œ ์ผํ•˜๊ฒŒ ๋ณต์›ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” inorder๊ฐ€ ํ•„์š”ํ•˜๋‹ค. inorder๋Š” left/right child๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
14+
- preorder + inorder: root๋ฅผ ๋จผ์ € ์•Œ๊ณ , inorder๋กœ ๊ตฌ์กฐ ๊ตฌ๋ถ„
15+
- postorder + postorder: root๋ฅผ ๋‚˜์ค‘์— ์•Œ๊ณ , inorder๋กœ ๊ตฌ์กฐ ๊ตฌ๋ถ„
16+
preorder + postorder๋ผ๋ฉด, ๊ตฌ์กฐ๊ฐ€ ๋‹ค๋ฅธ ํŠธ๋ฆฌ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.
17+
"""
18+
19+
class Solution:
20+
def buildTree1(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
21+
"""
22+
[Complexity]
23+
- TC: O(n^2)
24+
- SC: O(n^2)
25+
(* pop(0), index(), slicing์ด ๋ชจ๋‘ O(n))
26+
27+
[Approach]
28+
preorder์˜ ๊ฒฝ์šฐ, root - left - right ์ˆœ์„œ๋กœ ๋ฐฉ๋ฌธํ•˜๋ฏ€๋กœ, root๋ฅผ ์ œ์ผ ๋จผ์ € ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
29+
inorder์˜ ๊ฒฝ์šฐ, left - root - right ์ˆœ์„œ๋กœ ๋ฐฉ๋ฌธํ•˜๋ฏ€๋กœ,
30+
preorder์˜ ๋งค ๋‹จ๊ณ„์—์„œ ์ฐพ์€ root์— ๋Œ€ํ•ด left์™€ right subtree๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. (root ์ œ์™ธ)
31+
- left subtree = inorder[:root_idx]
32+
- right subtree = inorder[root_idx + 1:]
33+
"""
34+
root = None
35+
36+
if inorder:
37+
# preorder root
38+
root_idx = inorder.index(preorder.pop(0))
39+
root = TreeNode(val=inorder[root_idx])
40+
# preorder left
41+
root.left = self.buildTree(preorder, inorder[:root_idx])
42+
# preorder right
43+
root.right = self.buildTree(preorder, inorder[root_idx + 1:])
44+
45+
return root
46+
47+
def buildTree2(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
48+
"""
49+
[Complexity]
50+
- TC: O(n^2)
51+
- SC: O(n^2)
52+
(* index(), slicing์ด ๋ชจ๋‘ O(n))
53+
54+
[Approach]
55+
์ด์ „ ์ฝ”๋“œ์—์„œ pop(0)์— ๋“œ๋Š” O(n)์„ ์ค„์ด๊ธฐ ์œ„ํ•ด,
56+
preorder.reverse() ํ›„ pop()์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
57+
"""
58+
preorder.reverse()
59+
60+
def solve(preorder, inorder):
61+
if inorder:
62+
# preorder root
63+
root_idx = inorder.index(preorder.pop())
64+
root = TreeNode(inorder[root_idx])
65+
# preorder left
66+
root.left = solve(preorder, inorder[:root_idx])
67+
# preorder right
68+
root.right = solve(preorder, inorder[root_idx + 1:])
69+
70+
return root
71+
72+
return solve(preorder, inorder)
73+
74+
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
75+
"""
76+
[Complexity]
77+
- TC: O(n)
78+
- SC: O(n) (call stack)
79+
80+
[Approach]
81+
๋ณ‘๋ชฉ์ด์—ˆ๋˜ index()์™€ slicing์„ O(1)๋กœ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
82+
- index(): inorder์˜ index๋ฅผ dict๋กœ ์บ์‹ฑ
83+
- slicing: start/end index๋กœ ์ถ”์ 
84+
์ด์™€ ๋”๋ถˆ์–ด preorder์˜ root๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” index์ธ pre_idx๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด pop(0) ๋˜๋Š” pop()์„ ๋Œ€์ฒดํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
85+
"""
86+
87+
# instead of index()
88+
inorder_idx = {num: i for i, num in enumerate(inorder)}
89+
90+
# preorder root (instead of pop())
91+
pre_idx = 0
92+
93+
# instead of inorder[in_left:in_right] slicing
94+
def solve(in_left, in_right):
95+
nonlocal pre_idx
96+
97+
# base condition
98+
if in_left >= in_right:
99+
return
100+
101+
# preorder root
102+
root_val = preorder[pre_idx]
103+
root = TreeNode(root_val)
104+
105+
# update indices
106+
root_idx = inorder_idx[root_val]
107+
pre_idx += 1
108+
109+
# recur
110+
root.left = solve(in_left, root_idx)
111+
root.right = solve(root_idx + 1, in_right)
112+
113+
return root
114+
115+
return solve(0, len(inorder))
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# https://leetcode.com/problems/longest-palindromic-substring/
2+
3+
class Solution:
4+
def longestPalindrome_dp(self, s: str) -> str:
5+
"""
6+
[Complexity]
7+
- TC: O(n^2)
8+
- SC: O(n^2)
9+
10+
[Approach]
11+
(1) lo == hi: len 1์งœ๋ฆฌ substring -> True
12+
(2) lo + 1 == hi: len 2์งœ๋ฆฌ substring -> s[lo] == s[hi]
13+
(3) ๊ทธ ์™ธ: (a) ๋‚ด๋ถ€ string์ธ s[lo + 1] ~ s[hi - 1]์ด palindrome์ด๋ฉด์„œ,
14+
(b) s[lo] == s[hi]์ด๋ฉด True
15+
16+
=> (3-a)๋กœ ์ธํ•ด 2D DP๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ (dp[lo][hi] = s[lo] ~ s[hi]๊ฐ€ palindrome์ธ์ง€ ์—ฌ๋ถ€),
17+
dp[lo + 1][hi - 1]์„ ์‚ดํŽด๋ด์•ผ ํ•˜๋ฏ€๋กœ lo๋Š” **์˜ค๋ฅธ์ชฝ๋ถ€ํ„ฐ ๊ฑฐ๊พธ๋กœ ์ˆœํšŒ**ํ•ด์•ผ ํ•œ๋‹ค. (dp[lo][..]๋ฅผ ์ฑ„์šฐ๊ธฐ ์œ„ํ•ด dp[lo + 1][..]์„ ๋ด์•ผ ํ•จ)
18+
"""
19+
20+
n = len(s)
21+
res_lo = res_hi = 0
22+
dp = [[False] * n for _ in range(n)]
23+
24+
# early stop
25+
if n < 2 or s == s[::-1]:
26+
return s
27+
28+
for lo in range(n - 1, -1, -1):
29+
for hi in range(lo, n):
30+
# (1) lo == hi
31+
if lo == hi:
32+
dp[lo][hi] = True
33+
34+
# (2) lo + 1 == hi
35+
elif lo + 1 == hi:
36+
dp[lo][hi] = s[lo] == s[hi]
37+
38+
# (3) otherwise (a) dp[lo + 1][hi - 1] && (b) s[lo] == s[hi]
39+
else:
40+
dp[lo][hi] = dp[lo + 1][hi - 1] and s[lo] == s[hi]
41+
42+
# update res (s[lo:hi + 1]๊ฐ€ palindrome์ด๋ฉด์„œ, ์ตœ๋Œ€ ๊ธธ์ด์ธ ๊ฒฝ์šฐ)
43+
if dp[lo][hi] and res_hi - res_lo < hi - lo:
44+
res_lo, res_hi = lo, hi
45+
46+
return s[res_lo:res_hi + 1]
47+
48+
def longestPalindrome(self, s: str) -> str:
49+
"""
50+
[Complexity]
51+
- TC: O(n^2) (๊ฐ center์—์„œ ์–‘์ชฝ์œผ๋กœ expand)
52+
- SC: O(1)
53+
54+
[Approach]
55+
palindrome์€ ๋‹ค์Œ์˜ ๋‘ ์ผ€์ด์Šค๋กœ ๊ตฌ๋ถ„๋˜๋ฏ€๋กœ, two-pointer๋กœ๋„ ํ’€ ์ˆ˜ ์žˆ๋‹ค.
56+
(1) substring ๊ธธ์ด๊ฐ€ ํ™€์ˆ˜: ๊ฐ€์šด๋ฐ ์›์†Œ๋Š” ํ™•์ธํ•  ํ•„์š” X
57+
(2) substring ๊ธธ์ด๊ฐ€ ์ง์ˆ˜: ๋ชจ๋‘ ํ™•์ธ
58+
"""
59+
60+
n, res_lo, max_len = len(s), 0, 1
61+
62+
# early stop
63+
if n < 2 or s == s[::-1]:
64+
return s
65+
66+
# expanding from center
67+
def expand(lo, hi):
68+
# ์–‘์ชฝ์œผ๋กœ 1์”ฉ ๋Š˜๋ ค๊ฐ€๋ฉฐ ๊ฐ€์žฅ ๊ธด palindrome ์ฐพ๊ธฐ
69+
while lo >= 0 and hi < n and s[lo] == s[hi]:
70+
lo -= 1
71+
hi += 1
72+
return lo + 1, hi # s[lo + 1:hi]
73+
74+
# ๋ชจ๋“  ์œ„์น˜์—์„œ (1) ํ™€์ˆ˜ ๊ธธ์ด palindrome, (2) ์ง์ˆ˜ ๊ธธ์ด palindrome์˜ max len ๊ฐ’ ์ฐพ๊ธฐ
75+
for i in range(n - 1):
76+
# ํ™€์ˆ˜ ๊ธธ์ด ํŒฐ๋ฆฐ๋“œ๋กฌ (์ค‘์‹ฌ์ด i)
77+
lo1, hi1 = expand(i, i)
78+
if hi1 - lo1 > max_len:
79+
res_lo = lo1
80+
max_len = hi1 - lo1
81+
82+
# ์ง์ˆ˜ ๊ธธ์ด ํŒฐ๋ฆฐ๋“œ๋กฌ (์ค‘์‹ฌ์ด i, i+1)
83+
lo2, hi2 = expand(i, i + 1)
84+
if hi2 - lo2 > max_len:
85+
res_lo = lo2
86+
max_len = hi2 - lo2
87+
88+
return s[res_lo:res_lo + max_len]

โ€Žrotate-image/seungriyou.pyโ€Ž

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# https://leetcode.com/problems/rotate-image/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def rotate(self, matrix: List[List[int]]) -> None:
7+
"""
8+
Do not return anything, modify matrix in-place instead.
9+
"""
10+
"""
11+
[Complexity]
12+
- TC: O(n^2) (diagonal = n * (n - 1) / 2)
13+
- SC: O(1)
14+
15+
[Approach]
16+
clockwise rotation์„ in-place๋กœ ํ•˜๋ ค๋ฉด ๋‹ค์Œ์˜ ์ˆœ์„œ๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค.
17+
1. matrix reverse (by row)
18+
2. matrix transpose (diagonal)
19+
"""
20+
21+
n = len(matrix)
22+
23+
# 1. matrix reverse (by row)
24+
for r in range(n // 2):
25+
matrix[r], matrix[n - r - 1] = matrix[n - r - 1], matrix[r]
26+
27+
# 2. matrix transpose (diagonal)
28+
for r in range(n):
29+
for c in range(r):
30+
matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# https://leetcode.com/problems/subtree-of-another-tree/
2+
3+
from typing import Optional
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
class Solution:
13+
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
14+
"""
15+
[Complexity]
16+
- TC: O(n * m) (n = root ๋‚ด ๋…ธ๋“œ ๊ฐœ์ˆ˜, m = subRoot ๋‚ด ๋…ธ๋“œ ๊ฐœ์ˆ˜)
17+
- SC: O(n + m) (call stack)
18+
19+
[Approach]
20+
๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ž‘์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.
21+
(1) tree๋ฅผ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€๋Š” ๋กœ์ง : dfs
22+
(2) ๋‘ tree๊ฐ€ ๋™์ผํ•œ์ง€ ํŒ๋‹จํ•˜๋Š” ๋กœ์ง : check
23+
"""
24+
25+
def check(node1, node2):
26+
# base condition
27+
if not node1 and not node2:
28+
return True
29+
if not node1 or not node2:
30+
return False
31+
32+
# recur
33+
return (
34+
node1.val == node2.val # ๋‘ ๋…ธ๋“œ์˜ ๊ฐ’์ด ๊ฐ™๊ณ 
35+
and check(node1.left, node2.left) # ํ•˜์œ„ children๋„ ๋ชจ๋‘ ๊ฐ™์•„์•ผ ํ•จ
36+
and check(node1.right, node2.right)
37+
)
38+
39+
def dfs(node, sub_tree):
40+
# base condition
41+
if not node:
42+
return False
43+
44+
# recur
45+
return (
46+
check(node, sub_tree) # ํ˜„์žฌ node๋ถ€ํ„ฐ sub_tree์™€ ๋™์ผํ•œ์ง€ ํ™•์ธ
47+
or dfs(node.left, sub_tree) # ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋ฉด, node.left๋กœ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€์„œ ํ™•์ธ
48+
or dfs(node.right, sub_tree) # ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋ฉด, node.right๋กœ๋„ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€์„œ ํ™•์ธ
49+
)
50+
51+
return dfs(root, subRoot)

0 commit comments

Comments
ย (0)