From 247b03de224153a2c6a0a3d0adecc9dbdd0ca2ce Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sun, 26 Jul 2026 18:04:08 +0900 Subject: [PATCH 1/7] 20. Valid Parentheses --- valid-parentheses/okyungjin.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 valid-parentheses/okyungjin.py diff --git a/valid-parentheses/okyungjin.py b/valid-parentheses/okyungjin.py new file mode 100644 index 0000000000..4538206f9b --- /dev/null +++ b/valid-parentheses/okyungjin.py @@ -0,0 +1,30 @@ +""" +복잡도: + n: `s`의 길이 + 시간 복잡도: O(n) + 공간 복잡도: O(n) +""" +class Solution: + def isValid(self, s: str) -> bool: + # 괄호 개수가 짝수인 경우는 항상 실패 + if len(s) % 2: + return False + + brackets = { + ')' : '(', + '}' : '{', + ']' : '[' + } + + stack = [] + + for char in s: + if char in brackets: # 닫히는 괄호인지 + if stack and stack[-1] == brackets[char]: + stack.pop() + else: + stack.append(char) + else: + stack.append(char) + + return len(stack) == 0 From 040fa6c8c846701b9f2bee65043bbb666d6a58f6 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sun, 26 Jul 2026 20:20:38 +0900 Subject: [PATCH 2/7] 11. Container With Most Water --- container-with-most-water/okyungjin.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 container-with-most-water/okyungjin.py diff --git a/container-with-most-water/okyungjin.py b/container-with-most-water/okyungjin.py new file mode 100644 index 0000000000..5e86ba718c --- /dev/null +++ b/container-with-most-water/okyungjin.py @@ -0,0 +1,40 @@ +""" +문제: + - 담을 수 있는 최대 물의 양(최대 면적)을 구한다. + - i번째 수직선은 height[i] 높이를 가진다. + - x축과 선택한 두 수직선이 담는 물의 양이 최대가 되도록한다. + +접근법: + 1. 좌측, 우측 포인터 `left`, `right`를 선언하여 기둥을 움직인다. + 2. 좌우 기둥의 높이를 비교해서 높이가 낮은 기둥의 x좌표를 한칸 안쪽으로 움직인다. + 포인터를 바깥쪽에서 안쪽으로 이동할 때 가로의 길이가 줄어든다. + 따라서 낮은 기둥을 버려야 `max_area`를 갱신할 수 있는 가능성이 생긴다. + 3. 두 포인터가 만나거나 좌우가 역전되면 탐색을 종료한다. + +복잡도: + 시간 복잡도: O(n) + 공간 복잡도: O(1) + +실패했던 접근법: + 1. 2중 for: TLE + 2. 가장 높은 기둥을 고정축으로 하고 나머지 하나의 기둥을 찾도록: [1,2,1] 입력에서 실패 +""" +class Solution: + def maxArea(self, height: List[int]) -> int: + left = 0 + right = len(height) - 1 + + max_area = 0 + + while left < right: + if height[left] < height[right]: + area = height[left] * (right - left) + left += 1 + else: + area = height[right] * (right - left) + right -= 1 + + if area > max_area: + max_area = area + + return max_area From e764d8be80234ad2765821898a3e5ee02bcc7236 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 1 Aug 2026 00:59:57 +0900 Subject: [PATCH 3/7] 211. Design Add and Search Words Data Structure --- .../okyungjin.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 design-add-and-search-words-data-structure/okyungjin.py diff --git a/design-add-and-search-words-data-structure/okyungjin.py b/design-add-and-search-words-data-structure/okyungjin.py new file mode 100644 index 0000000000..8ece699ed3 --- /dev/null +++ b/design-add-and-search-words-data-structure/okyungjin.py @@ -0,0 +1,55 @@ +""" +solution1: 직관적인 풀이 +통과는 하지만 아주 느리다 + +Runetime: 5438ms, Beats 5.00% +Memory: 26.74MB, Beats 99.60% + +접근법: + addWord: `word_set`에 단어를 삽입한다. + search: `.`은 a-z로 변환해서 데카르트 곱을 통해 가능한 모든 문자열의 조합인 `candidates`를 만든다. `candidates`를 순회하며 `word_set`에 단어가 있는지 확인한다. +""" +import string +import itertools + +class WordDictionary: + def __init__(self): + self.word_set = set() + print(string.ascii_lowercase) + + """ + L: `word`의 길이 + N: `word_set`에 저장된 단어의 개수 + + Time: O(L), set.add() 메서드는 문자열 전체를 순회하며 해시 값을 계산해야 하므로 + Space: O(N*L) + """ + def addWord(self, word: str) -> None: + self.word_set.add(word) + + """ + L: `word`의 길이 + + Time: O(L) + Space: O(L) + """ + def search(self, word: str) -> bool: + candidates = [] + + """예시 + word: "bad", candidates: ['b', 'a', 'd'] + word: ".ad", candidates: ['abcdefghijklmnopqrstuvwxyz', 'a', 'd'] + word: "b..", candidates: ['b', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'] + """ + for c in word: + if c == '.': + candidates.append(string.ascii_lowercase) + else: + candidates.append(c) + + # 데카르트 곱으로 가능한 조합 생성힌다 + for p in itertools.product(*candidates): + if ''.join(p) in self.word_set: + return True + + return False From 6663c8267639effa73f5bdf405751484f6594387 Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 1 Aug 2026 15:47:30 +0900 Subject: [PATCH 4/7] =?UTF-8?q?211.=20Design=20Add=20and=20Search=20Words?= =?UTF-8?q?=20Data=20Structure=20-=20=EB=B2=84=ED=82=B7=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=ED=96=88?= =?UTF-8?q?=EC=9C=BC=EB=82=98=20TLE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../okyungjin.py | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/design-add-and-search-words-data-structure/okyungjin.py b/design-add-and-search-words-data-structure/okyungjin.py index 8ece699ed3..7712a377f8 100644 --- a/design-add-and-search-words-data-structure/okyungjin.py +++ b/design-add-and-search-words-data-structure/okyungjin.py @@ -1,13 +1,12 @@ """ -solution1: 직관적인 풀이 -통과는 하지만 아주 느리다 +solution1: 직관적인 풀이 (PASS인데 엄청 느리다) Runetime: 5438ms, Beats 5.00% Memory: 26.74MB, Beats 99.60% 접근법: addWord: `word_set`에 단어를 삽입한다. - search: `.`은 a-z로 변환해서 데카르트 곱을 통해 가능한 모든 문자열의 조합인 `candidates`를 만든다. `candidates`를 순회하며 `word_set`에 단어가 있는지 확인한다. + search: 와일드카드(`.`)은 a-z로 변환해서 데카르트 곱을 통해 가능한 모든 문자열의 조합인 `candidates`를 만든다. `candidates`를 순회하며 `word_set`에 단어가 있는지 확인한다. """ import string import itertools @@ -15,7 +14,6 @@ class WordDictionary: def __init__(self): self.word_set = set() - print(string.ascii_lowercase) """ L: `word`의 길이 @@ -36,7 +34,7 @@ def addWord(self, word: str) -> None: def search(self, word: str) -> bool: candidates = [] - """예시 + """Example: word: "bad", candidates: ['b', 'a', 'd'] word: ".ad", candidates: ['abcdefghijklmnopqrstuvwxyz', 'a', 'd'] word: "b..", candidates: ['b', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'] @@ -53,3 +51,57 @@ def search(self, word: str) -> bool: return True return False + +""" +Soultion2: `word`와 길이가 같은 단어들만 탐색하는 방법 +""" +from collections import defaultdict + + +class WordDictionary: + def __init__(self): + self.buckets: defaultdict[int, set[str]] = defaultdict(set) + + """ + L: `word`의 길이 + + Time: O(L) + Space: O(L) + """ + def addWord(self, word: str) -> None: + bucket = self.buckets[len(word)] + + if word not in bucket: + bucket.add(word) + + """ + L: `word`의 길이 + N: 같은 길이를 가진 저장된 단어의 개수 + + Time: O(L) - 와일드카드 없을 때 / O(N*L) - 와일드카드 있을 때 + Space: O(1) + """ + def search(self, word: str) -> bool: + # 글자수가 동일한 단어들만 탐색 + bucket: set[str] = self.buckets[len(word)] + + # 와일드카드 없을 때 + if '.' not in word: + return word in bucket + + for candidate in bucket: + if self._matches(word, candidate): + return True + + return False + + def _matches(self, word: str, candidate: str) -> bool: + for i, c in enumerate(word): + if c != '.' and c != candidate[i]: + return False + + return True + +# wordDictionary = WordDictionary() +# wordDictionary.addWord("bad") +# wordDictionary.search("..d") \ No newline at end of file From a28a02acdc84be4f3e86d535d5afa74f2e9f53bb Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 1 Aug 2026 16:28:37 +0900 Subject: [PATCH 5/7] =?UTF-8?q?211.=20Design=20Add=20and=20Search=20Words?= =?UTF-8?q?=20Data=20Structure=20-=20Trie=20=EC=9E=90=EB=A3=8C=EA=B5=AC?= =?UTF-8?q?=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 --- .../okyungjin.py | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/design-add-and-search-words-data-structure/okyungjin.py b/design-add-and-search-words-data-structure/okyungjin.py index 7712a377f8..96c2e917d9 100644 --- a/design-add-and-search-words-data-structure/okyungjin.py +++ b/design-add-and-search-words-data-structure/okyungjin.py @@ -53,55 +53,55 @@ def search(self, word: str) -> bool: return False """ -Soultion2: `word`와 길이가 같은 단어들만 탐색하는 방법 +Solution2: Trie 자료구조 활용 """ -from collections import defaultdict - - -class WordDictionary: +class Trie: def __init__(self): - self.buckets: defaultdict[int, set[str]] = defaultdict(set) + self.children: dict[str, Trie] = {} + self.is_end = False - """ - L: `word`의 길이 - Time: O(L) - Space: O(L) - """ def addWord(self, word: str) -> None: - bucket = self.buckets[len(word)] + curr = self - if word not in bucket: - bucket.add(word) + for char in word: + if char not in curr.children: + curr.children[char] = Trie() + curr = curr.children[char] - """ - L: `word`의 길이 - N: 같은 길이를 가진 저장된 단어의 개수 + curr.is_end = True - Time: O(L) - 와일드카드 없을 때 / O(N*L) - 와일드카드 있을 때 - Space: O(1) - """ - def search(self, word: str) -> bool: - # 글자수가 동일한 단어들만 탐색 - bucket: set[str] = self.buckets[len(word)] - # 와일드카드 없을 때 - if '.' not in word: - return word in bucket + def search(self, word: str) -> bool: + def _dfs(node: Trie, idx) -> bool: + if idx == len(word): + return node.is_end - for candidate in bucket: - if self._matches(word, candidate): - return True + char = word[idx] - return False + if char == '.': + return any( + _dfs(child, idx + 1) + for child in node.children.values()) - def _matches(self, word: str, candidate: str) -> bool: - for i, c in enumerate(word): - if c != '.' and c != candidate[i]: + if char not in node.children: return False - return True -# wordDictionary = WordDictionary() -# wordDictionary.addWord("bad") -# wordDictionary.search("..d") \ No newline at end of file + return _dfs(node.children[char], idx + 1) + + return _dfs(self, 0) + + +class WordDictionary: + def __init__(self): + self.wordDict = Trie() + + + def addWord(self, word: str) -> None: + self.wordDict.addWord(word) + + + def search(self, word: str) -> bool: + return self.wordDict.search(word) + \ No newline at end of file From 0ff54c44a0c3c94fd993b8dbb04461cd1805d81e Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 1 Aug 2026 16:46:55 +0900 Subject: [PATCH 6/7] =?UTF-8?q?211.=20Design=20Add=20and=20Search=20Words?= =?UTF-8?q?=20Data=20Structure=20-=20=EB=A6=B0=ED=8A=B8=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design-add-and-search-words-data-structure/okyungjin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/design-add-and-search-words-data-structure/okyungjin.py b/design-add-and-search-words-data-structure/okyungjin.py index 96c2e917d9..ed81b8c36a 100644 --- a/design-add-and-search-words-data-structure/okyungjin.py +++ b/design-add-and-search-words-data-structure/okyungjin.py @@ -104,4 +104,3 @@ def addWord(self, word: str) -> None: def search(self, word: str) -> bool: return self.wordDict.search(word) - \ No newline at end of file From a41de3ad0ba12754f6617bceb7743d4bd1cc49df Mon Sep 17 00:00:00 2001 From: KyungJin Jung Date: Sat, 1 Aug 2026 16:59:54 +0900 Subject: [PATCH 7/7] 300. Longest Increasing Subsequence --- longest-increasing-subsequence/okyungjin.py | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 longest-increasing-subsequence/okyungjin.py diff --git a/longest-increasing-subsequence/okyungjin.py b/longest-increasing-subsequence/okyungjin.py new file mode 100644 index 0000000000..7d66f67ef4 --- /dev/null +++ b/longest-increasing-subsequence/okyungjin.py @@ -0,0 +1,25 @@ +""" +https://leetcode.com/problems/longest-increasing-subsequence/ + +정수 배열 nums가 주어졌을 때, 가장 긴 증가하는 부분수열(연속하지 않아도 됨)의 길이를 구한다. + +1. `dp[i]`: `nums[i]`로 끝나는 증가 부분수열 중 최장 길이 +2. 모든 i에 대해, i보다 앞에 있고 값이 더 작은 j들의 dp[j] 중 최댓값 + 1을 dp[i]에 저장한다. +3. 답은 dp 배열 전체의 최댓값이다. + +Time: O(n^2) +Space: O(n) +""" +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + n = len(nums) + dp = [1] * n + + for i in range(1, n): + for j in range(i): + if nums[j] < nums[i] and dp[i] < dp[j] + 1: + dp[i] = dp[j] + 1 + + return max(dp) + +# TODO: O(n log n)으로 최적화 필요