Skip to content

Commit f471c4a

Browse files
authored
Merge pull request #2501 from liza0525/main
[liza0525] WEEK 05 Solutions
2 parents ae92369 + da5597a commit f471c4a

5 files changed

Lines changed: 151 additions & 0 deletions

File tree

best-time-to-buy-and-sell-stock/liza0525.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,25 @@ def maxProfit(self, prices: List[int]) -> int:
2323

2424
# 최종적으로 기록된 max_profit이 답
2525
return max_profit
26+
27+
28+
# 7기 풀이
29+
# 시간 복잡도: O(n)
30+
# - prices의 길이 n만큼 순회
31+
# 공간 복잡도: O(1)
32+
# - 상수만 사용
33+
class Solution:
34+
# 첫날부터 매일의 가격이 최소인지, 최대인지를 판단하여 가장 큰 이익을 낼 수 있는 값을 계산한다.
35+
# 첫날부터 시간 순서대로 확인해야 함(기간 중 가장 최댓값이 가장 최솟값보다 빠를 수도 있기 때문)
36+
def maxProfit(self, prices: List[int]) -> int:
37+
min_sell_price = 10 ** 4 # 문제 조건에서 10의 4승이 최대라고 하여 이를 min_sell_price의 초기값으로
38+
max_profit = 0
39+
40+
for price in prices:
41+
# 매일 순회하며 지나간 시간들 중에 가장 작은 price인지 확인하여 업데이트
42+
min_sell_price = min(min_sell_price, price)
43+
44+
# 매일 순회하며 오늘 얻을 수 있는 profit과 이전에 얻었던 max_profit을 비교하여 업데이트
45+
max_profit = max(max_profit, price - min_sell_price)
46+
47+
return max_profit
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# 7기 풀이
2+
# 시간 복잡도: O(n)
3+
# - 인코딩 디코딩 모두 문자열 리스트의 길이에 따라 시간 복잡도가 결정됨
4+
# 공간 복잡도: O(1)
5+
# - 정답 변수인 res를 제외하고는 변수만 쓰임
6+
7+
8+
DELIMITER = "^"
9+
10+
11+
class Solution:
12+
def encode(self, strs):
13+
# DELIMITER를 사이에 두고 단어의 글자 수와 단어를 concat해서 저장한다.
14+
# 예) ["cat", "is", "cute"] -> "3^cat2^is4^cute"
15+
# 이렇게 해야 글자 수를 예측하고 split하기 쉬워진다.
16+
return "".join(map(lambda x: f"{len(x)}{DELIMITER}{x}", strs))
17+
18+
def decode(self, s):
19+
res = []
20+
i = 0
21+
while i < len(s):
22+
# i번째 글자로부터 첫번째 DELIMITER가 나오는 인덱스를 j라고 칭함
23+
j = s.index(DELIMITER, i)
24+
word_len = int(s[i:j]) # DELIMITER 앞의 글자는 '글자 수'이므로 int로 변환
25+
word = s[j + 1:j + word_len + 1] # i번째부터 글자 수 만큼 잘라서 word를 찾음
26+
res.append(word)
27+
i = j + word_len + 1 # 자른 이후의 index를 새로운 i로 설정하여 다음 글자 찾음
28+
return res

group-anagrams/liza0525.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 7기 풀이
2+
# 시간 복잡도: O(n)
3+
# - 입력 받은 strs 리스트의 길이 만큼 순회하므로
4+
# 공간 복잡도: O(1)
5+
# - 결과로 반환할 result를 제외하고는 변수만 사용
6+
from collections import defaultdict
7+
8+
9+
class Solution:
10+
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
11+
result = defaultdict(list)
12+
13+
for str_ in strs:
14+
# 아나그램의 key를 동일하게 만든다. 갖고 있는 문자열의 오름차순 결과를 key로 만듦
15+
anagram_key = "".join(sorted(list(str_)))
16+
# 같은 key를 가진 것끼리 리스트로 묶어둔다.
17+
result[anagram_key].append(str_)
18+
19+
return list(result.values())
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 7기 풀이
2+
# Trie 전체 공간 복잡도: 공간: O(n * m)
3+
# - n은 단어 수, m은 평균 단어 길이
4+
# 각 메서드 마다 시간 복잡도와 공간 복잡도 작성
5+
class Node:
6+
def __init__(self, val=None):
7+
self.children = {} # 자식 노드를 담을 dictionary
8+
self.is_end = False # 현재 노드를 끝으로 하는 단어가 저장되었는지에 대한 flag
9+
10+
11+
class Trie:
12+
def __init__(self):
13+
self.root = Node()
14+
15+
def insert(self, word: str) -> None:
16+
# 시간: O(m) — m은 word의 길이, 문자 하나씩 순회
17+
# 공간: O(m) — 최악의 경우 m개의 새 노드 생성 (아무것도 공유 안할 때)
18+
node = self.root
19+
for s in word:
20+
if s not in node.children: # 아직 저장이 되지 않은 문자라면 children에 노드로써 저장해둔다.
21+
node.children[s] = Node(s)
22+
node = node.children[s] # 다음 노드를 children node로 지정하여 후손까지 저장할 수 있도록 함
23+
node.is_end = True # 모든 노드를 저장한 후에는 단어의 끝을 알리도록 is_end를 True로 변경
24+
25+
def search(self, word: str) -> bool:
26+
# 시간: O(m) — m은 word/prefix의 길이만큼 순회
27+
# 공간: O(1) — 새 노드 생성 없이 포인터만 이동
28+
node = self.root
29+
for s in word:
30+
if s not in node.children: # 찾으려는 단어의 문자가 저장되어 있지 않으면 search 실패이므로 False 반환
31+
return False
32+
node = node.children[s]
33+
34+
return node.is_end # 끝까지 돌아보고 해당 단어의 끝이 저장되어 있는지 아닌지를 반환하므로써 search의 성패 여부를 확인할 수 있음
35+
36+
def startsWith(self, prefix: str) -> bool:
37+
# start with의 경우에는, search와 거의 로직이 동일하나, is_end의 여부를 볼 필요가 없으므로 모든 문자에 대한 search가 끝나면 무조건 True 반환
38+
node = self.root
39+
for s in prefix:
40+
if s not in node.children:
41+
return False
42+
node = node.children[s]
43+
44+
return True
45+

word-break/liza0525.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 7기 풀이
2+
# 시간 복잡도: 시간복잡도: O(n * m)
3+
# - n은 s의 길이, m은 wordDict의 평균 단어 길이
4+
# - 각 인덱스마다 wordDict를 순회하므로
5+
# 공간 복잡도: 공간복잡도: O(n)
6+
# - memo에 최대 n개의 인덱스를 저장
7+
class Solution:
8+
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
9+
memo = set()
10+
11+
def dfs(i):
12+
if i == len(s):
13+
# i가 s의 길이가 되면 모든 탐색이 성공으로 끝났기 때문에
14+
# True로 반환
15+
return True
16+
17+
memo.add(i) # 해당 인덱스에 대해서는 방문을 했기 때문에 memo
18+
for word in wordDict:
19+
if i + len(word) in memo:
20+
# i + len(word)가 memoization해둔 인덱스라면 이미 성공한 케이스므로 통과
21+
continue
22+
23+
if i + len(word) > len(s):
24+
# i + len(word)가 len(s)보다 크면 탐색을 더이상 할 수 없으므로 continue
25+
continue
26+
27+
# 아직 방문을 하지 않은 index라면 단어를 slice하여 실제로 wordDict에 있는지 확인
28+
if s[i:i + len(word)] in wordDict:
29+
# 다음 인덱스 i + len(word)로 dfs 탐색
30+
check = dfs(i + len(word))
31+
if check:
32+
# 모든게 성공되었을 땐 True 반환
33+
return True
34+
35+
return False
36+
37+
return dfs(0)

0 commit comments

Comments
 (0)