From f9204a98b3439d6eecace7d08d0f280ee9544b6a Mon Sep 17 00:00:00 2001 From: obzva Date: Mon, 9 Sep 2024 23:41:28 +0900 Subject: [PATCH 1/5] Solution: Best Time to Buy and Sell Stock --- best-time-to-buy-and-sell-stock/flynn.py | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 best-time-to-buy-and-sell-stock/flynn.py diff --git a/best-time-to-buy-and-sell-stock/flynn.py b/best-time-to-buy-and-sell-stock/flynn.py new file mode 100644 index 000000000..24fd89e5f --- /dev/null +++ b/best-time-to-buy-and-sell-stock/flynn.py @@ -0,0 +1,40 @@ +''' +풀이 +- 주어진 배열 prices를 한 번 탐색합니다. + +- prices[i]가 이전에 저장한 buy_price보다 낮을 경우, + maximum profit이 증가할 가능성이 있습니다. + 따라서 buy_price와 sell_price를 prices[i]로 바꿔줍니다. + +- prices[i]가 이전에 저장한 sell_price보다 높은 경우, + 현재 buy_price로 지금까지 기록한 profit보다 더 높은 이익을 내게 됩니다. + 따라서 sell_price를 prices[i]로 바꿔주고 결과값을 갱신합니다. + +Big O +- N: 배열 prices의 크기 + +- Time complexity: O(N) + - 배열의 크기 N이 증가함에 따라 실행 시간도 선형적으로 증가합니다. + +- Space complexity: O(1) + - 배열의 크기 N이 증가하여도 사용하는 메모리 공간은 일정합니다. +''' + +class Solution: + def maxProfit(self, prices: List[int]) -> int: + res = 0 + + buy_price = prices[0] + sell_price = prices[0] + + for i in range(1, len(prices)): + curr_price = prices[i] + + if buy_price > curr_price: + buy_price = curr_price + sell_price = curr_price + elif sell_price < curr_price: + sell_price = curr_price + res = max(res, sell_price - buy_price) + + return res From 5210bb2d4cc2c14651a34922f24d94af940bab00 Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 10 Sep 2024 22:49:45 +0900 Subject: [PATCH 2/5] Solution: Group Anagrams --- group-anagrams/flynn.py | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 group-anagrams/flynn.py diff --git a/group-anagrams/flynn.py b/group-anagrams/flynn.py new file mode 100644 index 000000000..05cd86427 --- /dev/null +++ b/group-anagrams/flynn.py @@ -0,0 +1,42 @@ +''' +풀이 +- 두 단어가 anagram 관계라면 각 단어의 알파벳 수를 계산한 결과가 같을 것입니다 +- get_key 함수를 이용해서 단어를 이루는 알파벳 수에 따라 고유한 key를 생성합니다 +- key를 관리하는 해시맵인 key_map을 이용하여 현재 바라보고 있는 단어와 anagram인 단어가 있는지 확인합니다 + +Big O +- N: 배열 strs의 크기 +- K: 배열 strs의 원소 중 가장 길이가 긴 문자열의 길이 + +- Time complexity: O(N * K) + - 배열 strs를 순회합니다 -> N + - 각 문자열마다 알파벳의 수를 세기 위해 한 번 순회합니다 -> K + +- Space complexity: O(N) + - 배열 strs의 원소 모두 고유한 key를 지니고 있을 수 있습니다 + 이 경우 key_map의 크기는 N에 비례하여 선형적으로 증가할 수 있습니다 -> N +''' + +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + def get_key(s: str) -> str: + count = [0] * 26 + for char in s: + count[ord(char) - ord('a')] += 1 + res = "" + for c in count: + res += str(c) + "," + return res + + idx = 0 + key_map = {} + res = [] + for s in strs: + key = get_key(s) + if key not in key_map: + key_map[key] = idx + idx += 1 + res.append([]) + res[key_map[key]].append(s) + + return res From 09e16270219fde1c237787b9057013ed6a0355f1 Mon Sep 17 00:00:00 2001 From: obzva Date: Wed, 11 Sep 2024 15:09:19 +0900 Subject: [PATCH 3/5] Solution: 3 Sum --- 3sum/flynn.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 3sum/flynn.py diff --git a/3sum/flynn.py b/3sum/flynn.py new file mode 100644 index 000000000..40d42eaa5 --- /dev/null +++ b/3sum/flynn.py @@ -0,0 +1,48 @@ +''' +풀이 +- 중복되는 triplet을 피하기 위해 배열 nums를 정렬합니다 +- nums를 순회하며 이중 반복문을 수행하여 res 배열을 만듭니다 +- 중복되는 triplet을 피하기 위해 appended set을 이용합니다 + +Big O +- N: 배열 nums의 크기 + +- Time complexity: O(N^2) + - nums를 정렬하는데 걸리는 시간은 NlogN 형태로 증가합니다 + - 이중 반복문을 실행하는데 걸리는 시간은 N^2 형태로 증가합니다 + - O(NlogN + N^2)에서 증가율이 가장 큰 항은 N^2이므로 시간복잡도는 O(N^2)이라고 볼 수 있습니다 + +- Space complexity: O(N) + - nums를 정렬한 배열을 복사하여 sorted_nums에 저장하였고 이에 필요한 공간은 N의 형태로 증가합니다 + - 첫번째 반복문 안의 store은 최대 N만큼 커질 수 있습니다 + - appended 집합은 nums의 모든 원소가 고유하더라도 N보다 커질 수 없습니다 +''' + +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + n = len(nums) + + sorted_nums = sorted(nums) + + res = [] + + for i in range(n - 2): + first = sorted_nums[i] + + if i > 0 and first == sorted_nums[i - 1]: + continue + + store = {} + store[-first - sorted_nums[i + 1]] = sorted_nums[i + 1] + + appended = set() + + for j in range(i + 2, n): + second = sorted_nums[j] + + if second in store and second not in appended: + res.append([first, store[second], second]) + appended.add(second) + store[-first - second] = second + + return res From ea4e428b84df82b096d5c67f388218ee8a417921 Mon Sep 17 00:00:00 2001 From: obzva Date: Thu, 12 Sep 2024 16:33:01 +0900 Subject: [PATCH 4/5] Solution: Implement Trie Prefix Tree --- implement-trie-prefix-tree/flynn.py | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 implement-trie-prefix-tree/flynn.py diff --git a/implement-trie-prefix-tree/flynn.py b/implement-trie-prefix-tree/flynn.py new file mode 100644 index 000000000..b68428e86 --- /dev/null +++ b/implement-trie-prefix-tree/flynn.py @@ -0,0 +1,68 @@ +''' +풀이 +- Trie 자료구조 구현을 따릅니다 + +Big O +- N: 이전까지 insert한 word의 수 +- W: 현재 insert하거나 search하려는 word의 길이 +- P: 현재 startsWith하려는 prefix의 길이 + +- insert + - Time complexity: O(W) + - word를 구성하는 모든 문자에 대해 현재 Trie에 저장 여부를 검사합니다 + 해시맵 자료구조를 사용하고 있기에 검색과 삽입 모두 constant 시간 복잡도를 가집니다 + - Space complexity: O(W) + - 최악의 경우 모든 문자를 Trie에 새롭게 추가할 수 있습니다 +- search + - Time complexity: O(W) + - insert와 같습니다 + - Space complexity: O(1) +- startsWith + - Time complexity: O(P) + - search와 같습니다 + - Space complexity: O(1) +''' + +class Trie: + + def __init__(self): + self.root = {} + + def insert(self, word: str) -> None: + parent = self.root + + for c in word: + if c not in parent: + parent[c] = {} + parent = parent[c] + + parent["word"] = word + + def search(self, word: str) -> bool: + parent = self.root + + for c in word: + if c not in parent: + return False + parent = parent[c] + + return "word" in parent and parent["word"] == word + + + def startsWith(self, prefix: str) -> bool: + parent = self.root + + for c in prefix: + if c not in parent: + return False + parent = parent[c] + + 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) From a3b72bd83395caf31794f757e9ffbf2fb3a0b583 Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 13 Sep 2024 13:02:14 +0900 Subject: [PATCH 5/5] Solution: Word Break --- word-break/flynn.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 word-break/flynn.py diff --git a/word-break/flynn.py b/word-break/flynn.py new file mode 100644 index 000000000..9780b6796 --- /dev/null +++ b/word-break/flynn.py @@ -0,0 +1,51 @@ +''' +풀이 +- BFS와 방문 여부를 기록하는 방식을 이용하여 풀이할 수 있습니다 +- 주어진 문자열 s의 길이 n보다 1 더 큰 크기의 배열 visit을 False로 초기화해줍니다 + n + 1로 설정하는 이유는 BFS 설계를 쉽게 하기 위함입니다 +- queue 기능을 해줄 deque인 dq를 생성해주고 초기값으로 0을 삽입합니다 + dq의 원소 curr가 갖는 의미는 `현재 탐색 중인 substring의 시작 index, 즉 s[curr:]입니다` +- while문을 진행하며 dq에서 curr를 pop해줍니다 + 만약 curr를 탐색한 적이 있다면 지나치고, 탐색한 적이 없다면 starts_with 함수를 실행하고 + 그 여부에 따라 dq에 curr를 추가 및 visit을 갱신합니다 + +Big O +- N: 주어진 문자열 s의 길이 +- M: 주어진 문자열 배열 wordDict의 모든 문자열의 길이의 합 + +- Time complexity: O(N * M) + - visit 배열로 방문 여부를 관리하기 때문에 탐색은 최대 N번 이뤄집니다 + - 각 탐색마다 wordDict를 순회하며 starts_with를 실행하는데 이 실행 시간은 M이 증가함에 따라 선형적으로 증가합니다 + +- Space complexity: O(N) + - visit 배열의 크기는 N이 증가함에 따라 선형적으로 증가합니다 + - deque에 담긴 원소의 수는 최대 N까지 증가할 수 있습니다 +''' + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + n = len(s) + visit = [False] * (n + 1) + + def starts_with(idx: int, word: str) -> bool: + m = len(word) + for i in range(m): + if s[idx + i] != word[i]: + return False + return True + + dq = deque([0]) + + while dq: + curr = dq.popleft() + + if curr == len(s): + return True + + for word in wordDict: + m = len(word) + if curr + m <= n and not visit[curr + m] and starts_with(curr, word): + dq.append(curr + m) + visit[curr + m] = True + + return False