-
-
Notifications
You must be signed in to change notification settings - Fork 195
[EGON] Week 5 Solutions #456
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class Solution: | ||
def threeSum(self, nums: List[int]) -> List[List[int]]: | ||
return self.solveWithTwoPointer(nums) | ||
|
||
""" | ||
Runtime: 691 ms (Beats 62.42%) | ||
Time Complexity: O(n^2) | ||
- nums를 정렬하는데 O(n * log n) | ||
- 첫 index를 정하기 위해 range(len(nums) - 2) 조회하는데 O(n - 2) | ||
- i + 1 부터 n - 1까지 lo, hi 투포인터 조회하는데, i가 최소값인 0인 경우를 upper bound로 계산하면 O(n - 1) | ||
> O(n * log n) + O(n - 2) * O(n - 1) ~= O(n * log n) + O(n^2) ~= O(n^2) | ||
|
||
Memory: 20.71 MB (Beats 30.94%) | ||
Space Complexity: | ||
- num는 정렬하긴 했는데 자기자신 그대로 사용하므로 계산 외 | ||
> lo나 hi나 triplet_sum은 input에 영향없는 크기의 메모리를 사용하므로 O(1) | ||
""" | ||
|
||
def solveWithTwoPointer(self, nums: List[int]) -> List[List[int]]: | ||
nums.sort() | ||
triplets = [] | ||
|
||
for i in range(len(nums) - 2): | ||
if 1 <= i and nums[i] == nums[i - 1]: | ||
continue | ||
|
||
lo, hi = i + 1, len(nums) - 1 | ||
while lo < hi: | ||
triplet_sum = nums[i] + nums[lo] + nums[hi] | ||
|
||
if triplet_sum < 0: | ||
lo += 1 | ||
elif triplet_sum > 0: | ||
hi -= 1 | ||
else: | ||
triplets.append([nums[i], nums[lo], nums[hi]]) | ||
|
||
while lo < hi and nums[lo] == nums[lo + 1]: | ||
lo += 1 | ||
while lo < hi and nums[hi] == nums[hi - 1]: | ||
hi -= 1 | ||
|
||
lo += 1 | ||
hi -= 1 | ||
|
||
return triplets | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
nums = [-1, 0, 1, 2, -1, -4] | ||
output = [[-1, -1, 2], [-1, 0, 1]] | ||
self.assertEqual(Solution.threeSum(Solution(), nums), output) | ||
|
||
def test_2(self): | ||
nums = [0, 1, 1] | ||
output = [] | ||
self.assertEqual(Solution.threeSum(Solution(), nums), output) | ||
|
||
def test_3(self): | ||
strs = [0, 0, 0] | ||
output = [[0, 0, 0]] | ||
self.assertEqual(Solution.threeSum(Solution(), strs), output) | ||
|
||
def test_4(self): | ||
strs = [0, 0, 0, 0, 0, 0, 0, 0] | ||
output = [[0, 0, 0]] | ||
self.assertEqual(Solution.threeSum(Solution(), strs), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class Solution: | ||
def maxProfit(self, prices: List[int]) -> int: | ||
return self.solveWithStack(prices) | ||
|
||
""" | ||
Runtime: 749 ms (Beats 46.22%) | ||
Time Complexity: O(n) | ||
- prices의 길이 n 만큼 조회에 O(n) | ||
- 조회하며 수행하는 연산들은 .pop, .append, 고정된 크기에 대한 max 이므로 O(1) | ||
> O(n) * O(1) ~= O(n) | ||
|
||
Memory: 27.33 MB (Beats 91.14%) | ||
Space Complexity: O(1) | ||
- 크기가 1로 유지되는 stack: List[int] 사용 | ||
- max_profit: int 사용 | ||
> O(1) | ||
""" | ||
def solveWithStack(self, prices: List[int]) -> int: | ||
max_profit = 0 | ||
stack = [] | ||
for price in prices: | ||
if not stack: | ||
stack.append(price) | ||
continue | ||
|
||
min_price = stack.pop() | ||
if min_price < price: | ||
max_profit = max(price - min_price, max_profit) | ||
stack.append(min_price) | ||
elif min_price > price: | ||
stack.append(price) | ||
else: | ||
stack.append(min_price) | ||
|
||
return max_profit | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
prices = [7,1,5,3,6,4] | ||
output = 5 | ||
self.assertEqual(Solution.maxProfit(Solution(), prices), output) | ||
|
||
def test_2(self): | ||
prices = [7,6,4,3,1] | ||
output = 0 | ||
self.assertEqual(Solution.maxProfit(Solution(), prices), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class Solution: | ||
def groupAnagrams(self, strs: List[str]) -> List[List[str]]: | ||
return self.solveWithHashableKey(strs) | ||
|
||
""" | ||
Runtime: 88 ms (Beats 65.68%) | ||
Time Complexity: | ||
- strs의 길이 n 만큼 조회에 O(n) | ||
- string을 key로 변환하는데 string의 길이 L에 대해 O(L * log L) * O(L) | ||
- string의 최대 길이는 100이므로 O(100 * log 100) * O(100)가 upper bound | ||
- anagram_dict 갱신에 O(1) | ||
- 최대 크기가 n인 anagram_dict.values()를 list로 변환하는데 O(n^2) | ||
> O(n) * O(L * log L) * O(L) + O(n^2) ~= O(n^2) | ||
|
||
Memory: 19.32 MB (Beats 95.12%) | ||
Space Complexity: O(n * L) | ||
- 최대 n개의 key-value pair를 가질 수 있는 anagram_dict 사용, upper bound | ||
anagram_dict의 value는 최대 길이 L인 List[str] 이므로 O(n * L) | ||
> O(n * L) | ||
""" | ||
def solveWithHashableKey(self, strs: List[str]) -> List[List[str]]: | ||
anagram_dict = {} | ||
for string in strs: | ||
key = ''.join(sorted(string)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 시간복잡도를 O(L * log L) * O(L) 이라고 해주셨는데요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 길이가 L인 문자열에 대한 sorted에서 O(L * Log L), 정렬된 길이가 L인 배열을 하나의 문자열로 만드는데 O(L)이니 O(L * Log L) * O(L)이 맞는 것 같습니다 |
||
anagram_dict[key] = anagram_dict.get(key, []) + [string] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파이썬에서 기존배열에 다른 배열을 합치려면 이렇게 표현하는군요 👍 |
||
|
||
return list(anagram_dict.values()) | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
strs = ["eat","tea","tan","ate","nat","bat"] | ||
output = [["bat"],["nat","tan"],["ate","eat","tea"]] | ||
self.assertEqual(Solution.groupAnagrams(Solution(), strs), output) | ||
|
||
def test_2(self): | ||
strs = [""] | ||
output = [[""]] | ||
self.assertEqual(Solution.groupAnagrams(Solution(), strs), output) | ||
|
||
def test_3(self): | ||
strs = ["a"] | ||
output = [["a"]] | ||
self.assertEqual(Solution.groupAnagrams(Solution(), strs), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from unittest import TestCase, main | ||
|
||
|
||
""" | ||
Runtime: 136 ms (Beats 33.86%) | ||
Time Complexity: | ||
> insert: word의 각 문자마다 조회하므로 O(L) | ||
> search: 최대 word의 모든 문자를 조회하므로 O(L), upper bound | ||
> startsWith: 최대 prefix의 모든 문자를 조회하므로 O(L'), upper bound | ||
|
||
Memory: 32.60 MB (Beats 16.08%) | ||
Space Complexity: O(n * L), upper bound | ||
- 최악의 경우 삽입하는 모든 word가 공통점이 하나도 없는 경우를 상정해보면, 삽입하는 word의 갯수를 n, word의 최대 길이를 L | ||
> O(n * L), upper bound | ||
""" | ||
|
||
|
||
class Node: | ||
def __init__(self, key, data=None): | ||
self.key = key | ||
self.data = data | ||
self.children = {} | ||
|
||
|
||
class Trie: | ||
|
||
def __init__(self): | ||
self.root = Node(None) | ||
|
||
def insert(self, word: str) -> None: | ||
curr_node = self.root | ||
for char in word: | ||
if char not in curr_node.children: | ||
curr_node.children[char] = Node(char) | ||
|
||
curr_node = curr_node.children[char] | ||
|
||
curr_node.data = word | ||
|
||
def search(self, word: str) -> bool: | ||
curr_node = self.root | ||
for char in word: | ||
if char in curr_node.children: | ||
curr_node = curr_node.children[char] | ||
else: | ||
return False | ||
|
||
return curr_node.data == word | ||
|
||
def startsWith(self, prefix: str) -> bool: | ||
curr_node = self.root | ||
for char in prefix: | ||
if char in curr_node.children: | ||
curr_node = curr_node.children[char] | ||
else: | ||
return False | ||
else: | ||
return True | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
# commands = ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] | ||
# words = [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] | ||
# output = [None, None, True, False, True, None, True] | ||
|
||
trie = Trie() | ||
trie.insert("apple") | ||
self.assertEqual(trie.search("apple"), True) | ||
self.assertEqual(trie.search("app"), False) | ||
self.assertEqual(trie.startsWith("app"), True) | ||
trie.insert("app") | ||
self.assertEqual(trie.search("app"), True) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class Node: | ||
def __init__(self, key, data=None): | ||
self.key = key | ||
self.data = data | ||
self.children = {} | ||
|
||
|
||
class Trie: | ||
|
||
def __init__(self): | ||
self.root = Node(None) | ||
|
||
def insert(self, word: str) -> None: | ||
curr_node = self.root | ||
for char in word: | ||
if char not in curr_node.children: | ||
curr_node.children[char] = Node(char) | ||
|
||
curr_node = curr_node.children[char] | ||
|
||
curr_node.data = word | ||
|
||
def search(self, word: str) -> bool: | ||
curr_node = self.root | ||
for char in word: | ||
if char in curr_node.children: | ||
curr_node = curr_node.children[char] | ||
else: | ||
return False | ||
|
||
return curr_node.data == word | ||
|
||
def startsWith(self, prefix: str) -> bool: | ||
curr_node = self.root | ||
for char in prefix: | ||
if char in curr_node.children: | ||
curr_node = curr_node.children[char] | ||
else: | ||
return False | ||
else: | ||
return True | ||
|
||
|
||
class Solution: | ||
def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
return self.solveWithTrieDFSMemo(s, wordDict) | ||
|
||
""" | ||
Runtime: 38 ms (Beats 70.76%) | ||
Time Complexity: O(n * L + s^2) | ||
- s의 길이를 S, wordDict의 길이를 L, word의 최대 길이를 W라 하면 | ||
- trie에 insert하는데 O(W * L), upper bound | ||
- DFS에서 curr_s의 모든 문자 조회에 최대 O(S), 접두사를 제거하는 curr_s.removeprefix에서 최대 O(S) | ||
> O(W * L) + O(S) * O(S) = O(W * L + S^2) upper bound | ||
|
||
Memory: 17.70 MB (Beats 5.25%) | ||
Space Complexity: | ||
- trie 생성 및 갱신에 O(W * L) | ||
- visited는 최악의 경우 s의 모든 부분 문자열을 저장하므로 O(S^2) upper bound | ||
> O(W * L) + O(S^2) = O(W * L + S^2) upper bound | ||
""" | ||
def solveWithTrieDFSMemo(self, s: str, wordDict: List[str]) -> bool: | ||
trie = Trie() | ||
for word in wordDict: | ||
trie.insert(word) | ||
|
||
stack = [s] | ||
visited = set() | ||
while stack: | ||
curr_s = stack.pop() | ||
if curr_s in visited: | ||
continue | ||
|
||
curr_node = trie.root | ||
for char in curr_s: | ||
if char in curr_node.children: | ||
curr_node = curr_node.children[char] | ||
if curr_node.data is not None: | ||
post_s = curr_s.removeprefix(curr_node.data) | ||
if not post_s: | ||
return True | ||
else: | ||
stack.append(post_s) | ||
else: | ||
visited.add(curr_s) | ||
break | ||
|
||
return False | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
s = "leetcode" | ||
wordDict = ["leet","code"] | ||
output = True | ||
self.assertEqual(Solution.wordBreak(Solution(), s, wordDict), output) | ||
|
||
def test_2(self): | ||
s = "applepenapple" | ||
wordDict = ["apple","pen"] | ||
output = True | ||
self.assertEqual(Solution.wordBreak(Solution(), s, wordDict), output) | ||
|
||
def test_3(self): | ||
s = "catsandog" | ||
wordDict = ["cats","dog","sand","and","cat"] | ||
output = False | ||
self.assertEqual(Solution.wordBreak(Solution(), s, wordDict), output) | ||
|
||
def test_4(self): | ||
s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" | ||
wordDict = ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"] | ||
output = False | ||
self.assertEqual(Solution.wordBreak(Solution(), s, wordDict), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
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.
스택의 개념을 사용한 의미는 전달이 되는 코드인거같아요
이렇게 되면 스택의 최대 사이즈는 1밖에 산정이 안될텐데 스택을 선택한 이유가 궁금합니다.
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.
말씀하신 것처럼 스택을 썼음을 명시하려고 사용했습니다. 그냥 정수형 변수 하나를 사용하면 이 문제는 상관없겠지만, 문제를 풀며 생각한 사고 방식을 위해 스택을 썼습니다. 예를 들어 n개의 주식 혹은 다른 조건이 붙어도 스택을 사용하는 방식이 유효할 것이라 생각해서 사용했습니다.