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 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..ed81b8c36a --- /dev/null +++ b/design-add-and-search-words-data-structure/okyungjin.py @@ -0,0 +1,106 @@ +""" +solution1: 직관적인 풀이 (PASS인데 엄청 느리다) + +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() + + """ + 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 = [] + + """Example: + 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 + +""" +Solution2: Trie 자료구조 활용 +""" +class Trie: + def __init__(self): + self.children: dict[str, Trie] = {} + self.is_end = False + + + def addWord(self, word: str) -> None: + curr = self + + for char in word: + if char not in curr.children: + curr.children[char] = Trie() + curr = curr.children[char] + + curr.is_end = True + + + def search(self, word: str) -> bool: + def _dfs(node: Trie, idx) -> bool: + if idx == len(word): + return node.is_end + + char = word[idx] + + if char == '.': + return any( + _dfs(child, idx + 1) + for child in node.children.values()) + + if char not in node.children: + return False + + + 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) 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)으로 최적화 필요 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