-
-
Notifications
You must be signed in to change notification settings - Fork 195
[river20s] WEEK 05 solutions #1391
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
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
0703dff
feat: Add Solution to #221 Best Time to Buy And Sell Stock
river20s 8944b67
Merge branch 'DaleStudy:main' into main
river20s 7262bb7
feat: Add Solution to Group Anagrams #236
river20s 88ba4c1
feat: Add type hints to groupAnagrams method
river20s bd232b4
docs: Improve docstring and comments for groupAnagrams method
river20s a070e1b
Add Solution
river20s b7da025
fix: 파일 삭제
river20s ff51e87
docs: maxProfit 함수 주석 로직 순서 오류 수정
river20s 188ef5f
feat: Add Initial implementation to Word Break #271
river20s fcf21f7
perf: Apply memoization to optimize performance #271
river20s File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
class Solution(object): | ||
def maxProfit(self, prices): | ||
""" | ||
:type prices: List[int] | ||
:rtype: int | ||
주어진 prices를 한 번만 순회하면서 | ||
지금까지의 최솟값을 추적하고, | ||
'현재 가격 - 지금까지의 최솟값'으로 계산되는 이익이 | ||
'지금까지의 최대 이익(초기 0)보다 크면 갱신하여 | ||
최종 최대 이익 구하기 문제 | ||
Time Complexity: O(n) | ||
Space Complexity: O(1) | ||
""" | ||
max_profit = 0 # 이익이 없을 때 0을 반환하게 초기화 | ||
min_price = float('inf') # 최소 가격 저장, 무한대로 초기화해서 루프 첫 번째 가격부터 최소 가격에 저장 | ||
|
||
for price in prices: | ||
# 최대 이익 갱신 | ||
max_profit = max(max_profit, (price - min_price)) | ||
# 최소 가격 갱신 | ||
min_price = min(min_price, price) | ||
# 최대 이익(max_profit) 갱신 이후 최소 가격(min_price) 갱신해야 함 | ||
# 최대 이익 자체는 이미 '산' 주식에 대해 계산해야 하므로 | ||
# 사는 동시 팔 수 없음 | ||
|
||
return max_profit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import collections | ||
from typing import List | ||
|
||
class Solution(object): | ||
def groupAnagrams(self, strs: List[str]) -> List[List[str]]: | ||
""" | ||
:type strs: List[str] | ||
:rtype: List[List[str]] | ||
각 문자열을 순회하면서 알파벳 순으로 정렬한 결과를 tuple로 변환하여 Key로 하고, | ||
그 Key의 원래 문자열을 Value로 하는 딕셔너리를 통해 애너그램을 그룹화 하는 문제 | ||
딕셔너리의 Value만 모아서 반환함 | ||
Time Complexity: O(N*K log K) (단, N은 입력 리스트 안의 문자열 개수(리스트 길이), K는 문자열 하나 당 길이) | ||
-> 정렬에 O(K log K)시간 소요, 정렬 작업은 리스트 안의 문자열 개수 N만큼 반복 | ||
Space Complexity: O(N*K) | ||
-> 딕셔너리를 통해 원본 문자열 N개가 모두 저장됨 | ||
""" | ||
# collections의 defaultdict 사용하여 Key 없을 때 자동으로 빈 리스트 생성함 | ||
# Key: 정렬된 문자열, Value: 원래 문자열의 리스트 | ||
anagram_groups = collections.defaultdict(list) | ||
|
||
# s는 입력 strs의 각 문자열 | ||
# 모든 s에 대해 순회 | ||
for s in strs: | ||
# s를 알파벳 순으로 정렬하고, Key로 쓰기 위해 튜플로 변환함 | ||
# sorted(s)는 각 문자를 요소로 갖는 리스트(예: ['h', 'i']) | ||
# 튜플로 바꿔서 Key로 쓸 수 있게 하였음 | ||
key = tuple(sorted(s)) | ||
# defaultdict 사용하므로, Key 존재 여부 확인 불필요 | ||
# 해당 Key의 Value에 원래 문자열 s 추가 | ||
anagram_groups[key].append(s) | ||
# 딕셔너리 Value만 모아 반환 | ||
return list(anagram_groups.values()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
class TrieNode: | ||
# 트라이 노드 | ||
def __init__(self): | ||
self.children = {} | ||
# 단어의 끝을 나타내기 위한 플래그 | ||
self.isEndOfWord = False | ||
|
||
class Trie: | ||
# 트라이 | ||
def __init__(self): | ||
self.root = TrieNode() | ||
|
||
def insert(self, word: str) -> None: | ||
# 삽입 연산 | ||
# Time Complexity: O(L) (L은 word의 길이) | ||
# Space Complexity: O(M) (M은 트라이에 삽입된 모든 단어들의 총 문자 개수 합) | ||
currentNode = self.root | ||
|
||
for char in word: | ||
if char not in currentNode.children: | ||
currentNode.children[char] = TrieNode() | ||
currentNode = currentNode.children[char] | ||
currentNode.isEndOfWord = True | ||
|
||
def search(self, word: str) -> bool: | ||
# 완전 검색 연산 | ||
# Time Complexity: O(L) (L은 word의 길이) | ||
# Space Complexity: O(M) | ||
currentNode = self.root | ||
for char in word: | ||
if char not in currentNode.children: | ||
return False | ||
currentNode = currentNode.children[char] | ||
return currentNode.isEndOfWord | ||
|
||
def startsWith(self, prefix: str) -> bool: | ||
# 접두사 일치 검사 연산 | ||
# Time Complexity: O(P) (P는 Prefix의 길이) | ||
# Space Complexity: O(M) | ||
currentNode = self.root | ||
for char in prefix: | ||
if char not in currentNode.children: | ||
return False | ||
currentNode = currentNode.children[char] | ||
return True | ||
|
||
|
||
|
||
# Your Trie object will be instantiated and called as such: | ||
# obj = Trie() | ||
# obj.insert(word) | ||
# param_2 = obj.search(word) | ||
# param_3 = obj.startsWith(prefix) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
class TrieNode: | ||
def __init__(self): | ||
self.children = {} | ||
self.isEndOfWord = False | ||
|
||
class Trie: | ||
def __init__(self): | ||
self.root = TrieNode() | ||
|
||
def insert(self, word: str) -> None: | ||
currentNode = self.root | ||
for char in word: | ||
if char not in currentNode.children: | ||
currentNode.children[char] = TrieNode() | ||
currentNode = currentNode.children[char] | ||
currentNode.isEndOfWord = True | ||
|
||
# <-- 여기까지 Trie 구현을 위한 TrieNode와 Trie 클래스 | ||
# --> 여기부터 Word Break 문제 푸는 Solution 클래스 | ||
|
||
class Solution: | ||
def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
|
||
# 1. 트라이 구축 | ||
# wordDict 모든 단어 -> 트라이 넣기 | ||
trie = Trie() | ||
for word in wordDict: | ||
trie.insert(word) | ||
|
||
n = len(s) # 문자열 s의 길이, 나중에 인덱스 끝까지 도달했는지 확인하기 위해 사용함 | ||
|
||
# <<<--- 메모이제이션 캐시 초기화 ---<<< | ||
# key: start_index, value: s[start_index:] 분할 가능 여부 (True/False) | ||
memo = {} | ||
|
||
# 2. 재귀 함수 정의 | ||
# canBreak(start_index): s[strat_index:] 부분을 분할할 수 있는지 확인 | ||
def canBreak(start_index: int) -> bool: | ||
|
||
# <<<--- 캐시 확인 ---<<< | ||
# 이 start_index에 대한 결과가 이미 memo에 있으면 바로 반환 | ||
if start_index in memo: | ||
return memo[start_index] | ||
|
||
# 베이스 케이스 | ||
# 시작 인덱스(start_index)가 문자열 끝에 도달했다면 성공 | ||
if start_index == n: | ||
return True | ||
|
||
# 현재 start_index부터 시작하는 가능한 모든 단어를 트라이를 이용해 찾고 | ||
# 각 단어에 대해 나머지 부분이 분할 가능한지 재귀적으로 확인 | ||
currentNode = trie.root | ||
for i in range(start_index, n): | ||
char = s[i] | ||
|
||
# 현재 문자가 트라이 경로에 없다면 해당 트라이 탐색은 더이상 진행하지 않음 | ||
if char not in currentNode.children: | ||
break | ||
|
||
# 트라이의 다음 노드로 이동 | ||
currentNode = currentNode.children[char] | ||
|
||
# 이동한 노드가 단어의 끝이라면 | ||
if currentNode.isEndOfWord: | ||
# 나머지 부분 s[i+1:]에 대해서도 분할 가능한지 재귀 호출 | ||
if canBreak(i + 1): | ||
# 나머지 부분 분할 성공 => 전체 분할 가능 | ||
# <<<--- 성공 결과 캐시에 저장 ---<<< | ||
memo[start_index] = True | ||
return True | ||
# start_index부터 시작하는 모든 가능한 단어 분할을 시도했으나 | ||
# 성공적인 경로를 찾지 못했다면 | ||
# <<<--- 실패 결과 캐시에 저장 ---<<< | ||
memo[start_index] = False | ||
return False | ||
|
||
# 3. 재귀 함수 호출 시작 | ||
return canBreak(0) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"최소 이익 갱신 이후 최소 가격 갱신해야 함" 주석이 실제 코드 순서와 반대로 되어 있습니다.
코드에서는 최대 이익을 먼저 계산한 후 최소 가격을 갱신하고 있으므로, 주석과 맞게 조정하면 좋겠습니다 :)
전반적으로 매우 깔끔하고 효율적인 코드 같습니다. 고생하셨습니다👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감사합니다. 반영하여서 수정했습니다!