From af625107f7c87355f5aeb57347621eb8a4f5c00c Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Mon, 20 Jul 2026 20:14:47 +0900 Subject: [PATCH 01/10] 121. Best Time to Buy and Sell Stock --- best-time-to-buy-and-sell-stock/okyungjin.py | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 best-time-to-buy-and-sell-stock/okyungjin.py diff --git a/best-time-to-buy-and-sell-stock/okyungjin.py b/best-time-to-buy-and-sell-stock/okyungjin.py new file mode 100644 index 0000000000..83687e5126 --- /dev/null +++ b/best-time-to-buy-and-sell-stock/okyungjin.py @@ -0,0 +1,28 @@ +''' +[문제] +- 주식 가격이 담긴 정수 배열 prices가 주어진다. +- 특정 날짜에 주식을 단 한 번 구매하고, 미래의 특정 날짜에 판매하여 얻을 수 있는 최대 이익을 구한다. +- 이익을 낼 수 없는 경우 0을 반환하며, 반드시 구매 후 판매해야 한다. + +[풀이] +1. prices 배열을 순회하며 최소 가격을 min_price에 기록한다. +2. 동시에 현재 가격에서 min_price를 뺀 이익이 최대인지 확인하여 갱신한다. +3. 최대 이익을 반환한다. + +[복잡도] +시간 복잡도: O(N) +공간 복잡도: O(1) +''' +class Solution: + def maxProfit(self, prices): + min_price = prices[0] + max_profit = 0 + + for i in range(1, len(prices)): + if prices[i] < min_price: + min_price = prices[i] + + elif prices[i] - min_price > max_profit: + max_profit = prices[i] - min_price + + return max_profit From 39b3942194127eae3ed55c2c8f182e370c035f07 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Mon, 20 Jul 2026 21:00:35 +0900 Subject: [PATCH 02/10] 49. Group Anagrams --- group-anagrams/okyungjin.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 group-anagrams/okyungjin.py diff --git a/group-anagrams/okyungjin.py b/group-anagrams/okyungjin.py new file mode 100644 index 0000000000..cba49c3625 --- /dev/null +++ b/group-anagrams/okyungjin.py @@ -0,0 +1,20 @@ +''' +[복잡도] +n: strs의 길이, k: 각 문자열의 최대 길이 + +시간 복잡도: O(n * k log k) +공간 복잡도: O(n * k) +''' +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + anagram_map: dict[str, List[str]] = {} + + for s in strs: + key = ''.join(sorted(s)) + + if key in anagram_map: + anagram_map[key].append(s) + else: + anagram_map[key] = [s] + + return list(anagram_map.values()) From 615fffb2da17131814976c26050b1df15b199966 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Mon, 20 Jul 2026 22:36:31 +0900 Subject: [PATCH 03/10] 208. Implement Trie (Prefix Tree) --- implement-trie-prefix-tree/okyungjin.py | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 implement-trie-prefix-tree/okyungjin.py diff --git a/implement-trie-prefix-tree/okyungjin.py b/implement-trie-prefix-tree/okyungjin.py new file mode 100644 index 0000000000..1c7b497841 --- /dev/null +++ b/implement-trie-prefix-tree/okyungjin.py @@ -0,0 +1,26 @@ +class Trie: + + def __init__(self): + self.word_set = set() + self.prefix_set = set() + + + def insert(self, word: str) -> None: + if word in self.word_set: + return + + self.word_set.add(word) + + prefix = '' + for char in word: + prefix += char + self.prefix_set.add(prefix) + + + def search(self, word: str) -> bool: + return word in self.word_set + + + def startsWith(self, prefix: str) -> bool: + return prefix in self.prefix_set + \ No newline at end of file From 26972dc6de6bfe39eeb975b0adcd0551f3dafd21 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Mon, 20 Jul 2026 22:40:03 +0900 Subject: [PATCH 04/10] 208. Implement Trie (Prefix Tree) - fix linelint --- implement-trie-prefix-tree/okyungjin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/implement-trie-prefix-tree/okyungjin.py b/implement-trie-prefix-tree/okyungjin.py index 1c7b497841..9a8a64defb 100644 --- a/implement-trie-prefix-tree/okyungjin.py +++ b/implement-trie-prefix-tree/okyungjin.py @@ -23,4 +23,3 @@ def search(self, word: str) -> bool: def startsWith(self, prefix: str) -> bool: return prefix in self.prefix_set - \ No newline at end of file From 56d4bfc32a4cd39b1dc225430d6644832f61fc56 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Fri, 24 Jul 2026 00:59:11 +0900 Subject: [PATCH 05/10] Encode and Decode Strings --- encode-and-decode-strings/okyungjin.py | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 encode-and-decode-strings/okyungjin.py diff --git a/encode-and-decode-strings/okyungjin.py b/encode-and-decode-strings/okyungjin.py new file mode 100644 index 0000000000..48a3e6e747 --- /dev/null +++ b/encode-and-decode-strings/okyungjin.py @@ -0,0 +1,70 @@ +""" +Design an algorithm to encode a list of strings to a string. +The encoded string is then sent over the network and is decoded back to the original list of strings. + +Example 1: + Input: List[str] = ["Hello","World"] + Output: ["Hello","World"] + +Example 2: + Input: List[str] = [] + Output: [] + +Constraints: + 0 <= strs.length < 100 + 0 <= strs[i].length < 200 + strs[i] contains any possible characters out of 256 valid ASCII characters. +""" +from typing import Final, List +import ast + +""" +접근법: + encode: 각 단어 앞에 글자수 + 구분자를 붙여 하나의 문자열로 이어 붙입니다. (예: ["Hello", "World"] -> "5#Hello5#World") + decode: 문자열을 앞에서부터 읽으면서 구분자를 찾고, 그 앞의 숫자로 '글자수'를 알아낸 뒤 정확히 그 길이만큼만 잘라내어 원래 리스트로 복원합니다. + +복잡도: + 시간 복잡도: O(N) + 공간 복잡도: O(N) +""" +class Solution: + # 구분자 심볼 + DELIMITER: Final[str] = '#' + + """ + `글자수 + 구분자 + 단어` 조합으로 인코딩한 후 이어붙여 반환합니다. + + Example: + Input: ["Hello","World"] + Return: "5#Hello5#World" + + Input: [] + Return: "" + + Input: [""] + Return: "0#" + """ + def encode(self, strs: List[str]) -> str: + parts: List[str] = (f"{len(s)}{self.DELIMITER}{s}" for s in strs) + + return ''.join(parts) + + + """ + 1. idx 이후에 구분자가 등장하는 인덱스를 찾습니다. + 2. 구분자 바로 앞에 있는 문자의 길이를 추출합니다. + 3. 구분자 이후 ~ 문자의 길이 만큼 슬라이스해서 대상 단어를 추출합니다. 추출한 단어는 result에 담습니다. + 4. result를 반환합니다. + """ + def decode(self, s: str) -> List[str]: + result = [] + idx = 0 + + while idx < len(s): + delimiter_idx = s.find(self.DELIMITER, idx) + length = int(s[idx : delimiter_idx]) + + idx = delimiter_idx + 1 + length + result.append(s[delimiter_idx + 1 : idx]) + + return result From 3ab45db55c5b79e9b674fc74a45c57218d6df859 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Fri, 24 Jul 2026 01:05:11 +0900 Subject: [PATCH 06/10] =?UTF-8?q?Encode=20and=20Decode=20Strings=20(?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- encode-and-decode-strings/okyungjin.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/encode-and-decode-strings/okyungjin.py b/encode-and-decode-strings/okyungjin.py index 48a3e6e747..7303029fc5 100644 --- a/encode-and-decode-strings/okyungjin.py +++ b/encode-and-decode-strings/okyungjin.py @@ -16,11 +16,10 @@ strs[i] contains any possible characters out of 256 valid ASCII characters. """ from typing import Final, List -import ast """ 접근법: - encode: 각 단어 앞에 글자수 + 구분자를 붙여 하나의 문자열로 이어 붙입니다. (예: ["Hello", "World"] -> "5#Hello5#World") + encode: 각 단어 앞에 글자수 + 구분자를 붙여 하나의 문자열로 이어 붙입니다. decode: 문자열을 앞에서부터 읽으면서 구분자를 찾고, 그 앞의 숫자로 '글자수'를 알아낸 뒤 정확히 그 길이만큼만 잘라내어 원래 리스트로 복원합니다. 복잡도: @@ -45,16 +44,14 @@ class Solution: Return: "0#" """ def encode(self, strs: List[str]) -> str: - parts: List[str] = (f"{len(s)}{self.DELIMITER}{s}" for s in strs) - - return ''.join(parts) - + return ''.join(f"{len(s)}{self.DELIMITER}{s}" for s in strs) + """ - 1. idx 이후에 구분자가 등장하는 인덱스를 찾습니다. - 2. 구분자 바로 앞에 있는 문자의 길이를 추출합니다. - 3. 구분자 이후 ~ 문자의 길이 만큼 슬라이스해서 대상 단어를 추출합니다. 추출한 단어는 result에 담습니다. - 4. result를 반환합니다. + 1. 현재 위치부터 탐색하여 첫 번째 구분자(#)의 위치를 찾습니다. + 2. 구분자 바로 앞의 숫자를 읽어내어 잘라낼 단어의 길이를 파악합니다. + 3. 구분자 다음 위치부터 파악한 길이만큼 문자열을 잘라내어 결과 리스트에 담습니다. + 4. 문자열 끝까지 위 과정을 반복한 후, 최종 리스트를 반환합니다. """ def decode(self, s: str) -> List[str]: result = [] From a0a7154d076ff21928524df32f85d2933f469bf1 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 25 Jul 2026 18:47:04 +0900 Subject: [PATCH 07/10] 139. Word Break --- word-break/okyungjin.py | 99 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 word-break/okyungjin.py diff --git a/word-break/okyungjin.py b/word-break/okyungjin.py new file mode 100644 index 0000000000..d9696ef9a1 --- /dev/null +++ b/word-break/okyungjin.py @@ -0,0 +1,99 @@ +''' +solution1: dfs + @cache +''' +from typing import List +from functools import cache + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + size = len(s) + word_set = set(wordDict) + + @cache + def dfs(start: int) -> bool: + if start == size: + return True + + for end in range(start + 1, size + 1): + word = s[start:end] # O(N) + + if word in word_set and dfs(end): + return True + + return False + + return dfs(0) + +''' +solution2: dfs + memo +''' +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + size = len(s) + word_set = set(wordDict) + failed: set[int] = set() # 탐색에 실패한 인덱스 기록 + + def dfs(start: int) -> bool: + if start == size: + return True + + if start in failed: + return False + + for end in range(start + 1, size + 1): + word = s[start:end] + + if word in word_set and dfs(end): + return True + + failed.add(start) + return False + + return dfs(0) + +''' +solution3: bfs +''' +from collections import deque + +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + size = len(s) + word_set = set(wordDict) + queue = deque([0]) + visited = set([0]) + + while queue: + start = queue.popleft() + + for end in range(start + 1, size + 1): + word = s[start:end] + + if end not in visited and word in word_set: + if end == size: + return True + + queue.append(end) + visited.add(end) + + return False + +''' +solution4: dp +''' +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + size = len(s) + word_set = set(wordDict) + dp = [False] * (size + 1) + dp[0] = True + + for end in range(1, size + 1): + for start in range(end): + word = s[start:end] + + if dp[start] and word in word_set: + dp[end] = True + break + + return dp[-1] From 52ec55332b7d69c7c8d02a091f728faf3e30eaaf Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 25 Jul 2026 19:08:10 +0900 Subject: [PATCH 08/10] =?UTF-8?q?208.=20Implement=20Trie=20(Prefix=20Tree)?= =?UTF-8?q?=20-=20TrieNode=20=EC=9E=90=EB=A3=8C=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- implement-trie-prefix-tree/okyungjin.py | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/implement-trie-prefix-tree/okyungjin.py b/implement-trie-prefix-tree/okyungjin.py index 9a8a64defb..74e4fbe3b3 100644 --- a/implement-trie-prefix-tree/okyungjin.py +++ b/implement-trie-prefix-tree/okyungjin.py @@ -23,3 +23,58 @@ def search(self, word: str) -> bool: def startsWith(self, prefix: str) -> bool: return prefix in self.prefix_set + + +''' +solution2: TrieNode 자료구조 활용 +''' +class TrieNode: + def __init__(self): + self.children: dict[str, 'TrieNode'] = {} + self.is_leaf = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + ''' + 시간 복잡도: O(L), L: 단어의 길이 + 공간 복잡도: O(L), 겹치는 문자열이 없을 때 TrideNode N개 생성 + ''' + def insert(self, word: str) -> None: + node = self.root + + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + + node.is_leaf = True + + ''' + 시간 복잡도: O(L), L: 단어의 길이 + 공간 복잡도: O(1) + ''' + def search(self, word: str) -> bool: + node = self.root + + for char in word: + if char not in node.children: + return False + node = node.children[char] + + return node.is_leaf + + ''' + 시간 복잡도: O(L), L: 단어의 길이 + 공간 복잡도: O(1) + ''' + def startsWith(self, prefix: str) -> bool: + node = self.root + + for char in prefix: + if char not in node.children: + return False + node = node.children[char] + + return True From bfc447f3e0fc609214e4d0b9bc54a17d16b1fe8a Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 25 Jul 2026 19:26:32 +0900 Subject: [PATCH 09/10] =?UTF-8?q?139.=20Word=20Break=20-=20Trie=20?= =?UTF-8?q?=EC=9E=90=EB=A3=8C=EA=B5=AC=EC=A1=B0=20=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- word-break/okyungjin.py | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/word-break/okyungjin.py b/word-break/okyungjin.py index d9696ef9a1..dcb7975aec 100644 --- a/word-break/okyungjin.py +++ b/word-break/okyungjin.py @@ -97,3 +97,60 @@ def wordBreak(self, s: str, wordDict: list[str]) -> bool: break return dp[-1] + +''' +solution5: trie 자료구조 활용 + +Trie 자료구조로 노드를 만들어서 탐색한다 +Examples3: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"] + +[Root] + ├── 'c' ── 'a' ── 't'(leaf) ── 's'(leaf) + ├── 'd' ── 'o' ── 'g'(leaf) + ├── 's' ── 'a' ── 'n' ── 'd'(leaf) + └── 'a' ── 'n' ── 'd'(leaf) +''' +class TrieNode: + def __init__(self): + self.children: dict[int, 'TrieNode'] = {} + self.is_leaf = False + +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + root = self._build_trie(wordDict) + + size = len(s) + dp = [False] * (size + 1) + dp[0] = True + + for start in range(size): + if not dp[start]: + continue + + node = root + for end in range(start + 1, size + 1): + char = s[end - 1] + # trie에 없으면 바로 종료 + if char not in node.children: + break + node = node.children[char] + + if node.is_leaf: + dp[end] = True + + return dp[-1] + + # wordDict로 trie 자료구조 생성 + def _build_trie(self, wordDict: list[str]) -> TrieNode: + root = TrieNode() + + for word in wordDict: + node = root + + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + node.is_leaf = True + + return root From 84fbf28b29551187db6d7cc4890d7e02fe1db5ec Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 25 Jul 2026 20:12:39 +0900 Subject: [PATCH 10/10] =?UTF-8?q?49.=20Group=20Anagrams=20-=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EB=B3=B5=EC=9E=A1=EB=8F=84=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group-anagrams/okyungjin.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/group-anagrams/okyungjin.py b/group-anagrams/okyungjin.py index cba49c3625..88fbf5dd22 100644 --- a/group-anagrams/okyungjin.py +++ b/group-anagrams/okyungjin.py @@ -18,3 +18,27 @@ def groupAnagrams(self, strs: List[str]) -> List[List[str]]: anagram_map[key] = [s] return list(anagram_map.values()) + + +''' +solution2: 문자열의 빈도수 배열을 해시의 키로 사용 + +복잡도: + n: strs의 길이, k: 각 문자열의 최대 길이 + 시간 복잡도: O(n * k) + 공간 복잡도: O(n * k) +''' +from collections import defaultdict + +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + anagram_map: dict[tuple, List[str]] = defaultdict(list) + + for s in strs: + counts = [0] * 26 + for char in s: + counts[ord(char) - ord('a')] += 1 + + anagram_map[tuple(counts)].append(s) + + return list(anagram_map.values())