diff --git a/combination-sum/okyungjin.py b/combination-sum/okyungjin.py new file mode 100644 index 0000000000..c9dd3566c6 --- /dev/null +++ b/combination-sum/okyungjin.py @@ -0,0 +1,73 @@ +# https://leetcode.com/problems/combination-sum/ + +# [접근] +# 처음에는 1부터 target 까지 결과를 저장하는 dp를 떠올렸다. 이 문제는 조합의 개수를 구하는게 아니라 전체 후보군을 구하는 문제이기 때문에 dfs로 백트래킹하도록 접근법을 바꿨다. + +class Solution_01: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + result = [] + size = len(candidates) + + # start_idx: 탐색 시작 인덱스 + # total: 현재까지의 합 + # combination: 선택된 숫자들 배열 + def dfs(start_idx: int, total: int, combination: List[int]) -> None: + # 합이 target과 같으면 정답 배열에 추가하고 종료 + if total == target: + result.append(combination) + return + + # 합이 target 넘었으면 종료 + if total > target: + return + + # 숫자를 여러번 사용할 수 있으므로 반복문을 start_idx 부터 시작한다. + for i in range(start_idx, size): + num = candidates[i] + dfs( + i, + total + num, # num 을 더한 합으로 호출 + combination + [num]) # 얕은 복사된 combination을 인자로 넘긴다. + + dfs(0, 0, []) + + return result + + +# 위 풀이와 거의 유사하다. +# 백트래킹시에 배열이 매번 깊은 복사되는 비효율이 있어서 result 배열에 담을 때만 복사하도록 했다. +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + result = [] + size = len(candidates) + + # start_idx: 탐색 시작 인덱스 + # total: 현재까지의 합 + # combination: 선택된 숫자들 배열 + def dfs(start_idx: int, total: int, combination: List[int]) -> None: + # result + if total == target: + # combination을 얕은 복사 후 추가한다 + result.append(combination.copy()) + return + + # 합이 target 넘었으면 종료 + if total > target: + return + + # 숫자를 여러번 사용할 수 있으므로 반복문을 start_idx 부터 시작한다. + for i in range(start_idx, size): + num = candidates[i] + + # 반환 전 num 추가 + # -> 정답 찾으면 result에 추가할 때 combination 얕은 복사 필요 + combination.append(num) + + dfs(i, total + num, combination) + + # 반환 후에 num 제거 + combination.pop() + + dfs(0, 0, []) + + return result diff --git a/number-of-1-bits/okyungjin.py b/number-of-1-bits/okyungjin.py new file mode 100644 index 0000000000..e3507ed200 --- /dev/null +++ b/number-of-1-bits/okyungjin.py @@ -0,0 +1,23 @@ +# https://leetcode.com/problems/number-of-1-bits/description/ + +# n을 2진법으로 변환했을 때 1의 개수를 세는 문제이다. +# `n & 1` 연산을 통해 마지막 비트가 1인지 검사해서 count를 증가시킨다. +# 확인한 비트를 `n >> 1` 으로 한자리 버린다. + +class Solution: + def hammingWeight(self, n: int) -> int: + count = 0 + + while n: + # `n & 1` 연산을 통해 마지막 비트가 1인지 검사해서 count를 증가시킨다. + + # 1011 + # & 0001 + # ------ + # 0001 -> 결과 1이 된다 + count += n & 1 + + # 비트 한자리 이동한다 (1011 -> 0101) + n = n >> 1 + + return count diff --git a/valid-palindrome/okyungjin.py b/valid-palindrome/okyungjin.py new file mode 100644 index 0000000000..fe43c06ea5 --- /dev/null +++ b/valid-palindrome/okyungjin.py @@ -0,0 +1,85 @@ +# [문제] +# https://leetcode.com/problems/valid-palindrome/description/ + +# [요구사항] +# 주어진 문자열이 팰린드롬인지 판별하는 함수를 작성한다. +# 팰린드롬이란? +# 1. 대문자가 있다면 소문자로 변환한다 +# 2. 알파벳 or 숫자가 아니라면 제거한다. +# 3. 앞뒤로 읽었을 때 동일한 문자여야 한다. + +# [접근법] +# - 문자열 전체를 공백 제거 & 소문자로 변환하지 않고, 투 포인터를 통해 lazy하게 연산한다. +# - 문자열 양 끝에 포인터를 선언하고, alphanumeric인 경우 소문자로 변환 후 두 문자를 비교한다. 다르면 False 리턴 +# - alphanumeric이 아니면 alphanumeric 문자가 나올 때까지 이동한다. + +# [복잡도] +# 시간 복잡도: O(N) +# 공간 복잡도: O(1) +class SolutionA: + def isPalindrome(self, s: str) -> bool: + + # 문자열 양 끝 포인터 선언 + left = 0 + right = len(s) - 1 + + while left < right: + # alphanumeric 이 나올 때까지 왼쪽 포인터를 우측으로 이동 + while left < right and not isAlphaNumeric(s[left]): + left += 1 + + # alphanumeric 이 나올 때까지 오른쪽 포인터를 좌측으로 이동 + while left < right and not isAlphaNumeric(s[right]): + right -= 1 + + # 소문자로 변환해서 비교 + if s[left].lower() != s[right].lower(): + return False + + left += 1 + right -= 1 + + return True + +# 문자가 alphanumeric인지 판별하는 함수 +# A ~ Z 혹은 a ~ z 혹은 0 ~ 9 이면 True를 반환 +def isAlphaNumeric(ch: str) -> bool: + return ( + ord('A') <= ord(ch) <= ord('Z') + or ord('a') <= ord(ch) <= ord('z') + or ord('0') <= ord(ch) <= ord('9') + ) + + +# SolutuionB: SoulutionA와 같은 로직인데 `isalnum` 내장함수 사용 +# isAlphaNumeric 함수를 직접 구현하고 찾아보니 파이썬 내장함수가 있었다. + +# [복잡도] +# 시간 복잡도: O(N) +# 공간 복잡도: O(1) +class Solution: + def isPalindrome(self, s: str) -> bool: + # 문자열 양 끝 포인터 선언 + left = 0 + right = len(s) - 1 + + while left < right: + # alphanumeric 이 나올 때까지 왼쪽 포인터를 우측으로 이동 + while left < right and not s[left].isalnum(): + left += 1 + + # alphanumeric 이 나올 때까지 오른쪽 포인터를 좌측으로 이동 + while left < right and not s[right].isalnum(): + right -= 1 + + # 소문자로 변환해서 비교 + if s[left].lower() != s[right].lower(): + return False + + left += 1 + right -= 1 + + return True + + +print(Solution().isPalindrome('A man, a plan, a canal: Panama'))