From 737b202c9a7ce70c257d3a2069f161ec6c0bb2d5 Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 22 Nov 2025 10:08:39 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20valid-palindrome=20=ED=92=80?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- valid-palindrome/unpo88.py | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 valid-palindrome/unpo88.py diff --git a/valid-palindrome/unpo88.py b/valid-palindrome/unpo88.py new file mode 100644 index 0000000000..e130ea3f42 --- /dev/null +++ b/valid-palindrome/unpo88.py @@ -0,0 +1,66 @@ +class Solution: + def isPalindrome(self, s: str) -> bool: + left = 0 + right = len(s) - 1 + + while left < right: + if not s[left].isalnum(): + left += 1 + continue + + if not s[right].isalnum(): + right -= 1 + continue + + if s[left].upper() != s[right].upper(): + return False + + left += 1 + right -= 1 + + return True + + +""" +================================================================================ +풀이 과정 +================================================================================ +- A man, a plan, a canal: Panama +- 띄어쓰기는 무시하고 앞 뒤에서 똑같은지 체크를 해야하네? +- 앞 포인터와 뒷 포인터에서 시작해서 띄어쓰기 만나면 건너뛰고 +- 틀린것 나오면 False 반환하고, False를 만난적 없으면 True 반환 + +[1차 시도] Two Pointer +──────────────────────────────────────────────────────────────────────────────── +1. 접근 방법 + - Two Pointer 사용: left는 앞에서, right는 뒤에서 시작 + - 유효하지 않은 문자(알파벳/숫자가 아닌 것)는 건너뛰기 + - 유효한 문자끼리 비교하며 중앙으로 이동 + +2. 구현 + left = 0 + right = len(s) - 1 + + while left < right: + # 왼쪽 포인터: 알파벳/숫자가 아니면 건너뛰기 + if not s[left].isalpha() and not s[left].isnumeric(): + left += 1 + continue + + # 오른쪽 포인터: 알파벳/숫자가 아니면 건너뛰기 + if not s[right].isalpha() and not s[right].isnumeric(): + right -= 1 + continue + + # 대소문자 무시하고 비교 + if s[left].upper() != s[right].upper(): + return False + + left += 1 + right -= 1 + + return True + +4. 시간 복잡도: O(n) - 문자열을 한 번만 순회 +5. 공간 복잡도: O(1) - 추가 공간 사용 없음 (포인터 2개만 사용) +""" \ No newline at end of file From b8b94a39fc5dfc9806336a5afcc65a807357cfdf Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 22 Nov 2025 10:26:55 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20number-of-1-bits=20=ED=92=80?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- number-of-1-bits/unpo88.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 number-of-1-bits/unpo88.py diff --git a/number-of-1-bits/unpo88.py b/number-of-1-bits/unpo88.py new file mode 100644 index 0000000000..328d65311f --- /dev/null +++ b/number-of-1-bits/unpo88.py @@ -0,0 +1,24 @@ +class Solution: + def hammingWeight(self, n: int) -> int: + return bin(n).count('1') + + +""" +================================================================================ +풀이 과정 +================================================================================ + +1. 일단 이진법으로 변경이 필요하고 +2. 변경된 이진법에서 1을 카운팅해야하네? + + +[1차 시도] 내장 함수 활용 +──────────────────────────────────────────────────────────────────────────────── +3. Python의 bin() 함수로 이진 문자열 변환 +4. count() 메서드로 '1' 문자 개수 세기 + + return bin(n).count('1') + +5. 시간 복잡도: O(log n) - 이진 표현의 비트 수만큼 +6. 공간 복잡도: O(log n) - 이진 문자열 생성 +""" \ No newline at end of file From 2db0603cb3e954a9238d0954d8c269c24b796828 Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 22 Nov 2025 10:40:40 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20combination-sum=20=ED=92=80?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- combination-sum/unpo88.py | 77 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 combination-sum/unpo88.py diff --git a/combination-sum/unpo88.py b/combination-sum/unpo88.py new file mode 100644 index 0000000000..492444170b --- /dev/null +++ b/combination-sum/unpo88.py @@ -0,0 +1,77 @@ +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + result = [] + + def backtrack(start, current, current_sum): + if current_sum == target: + result.append(current[:]) + return + + if current_sum > target: + return + + for i in range(start, len(candidates)): + current.append(candidates[i]) + backtrack(i, current, current_sum + candidates[i]) + current.pop() + + backtrack(0, [], 0) + return result + + +""" +================================================================================ +풀이 과정 +================================================================================ + +1. 이거 모든 가능한 조합을 찾아야하는데 합이 target을 넘으면 중단되야하네? +2. 문제는 같은 숫자를 여러 번 쓸 수 있는건데.. +3. 백트래킹으로 접근해야할 것 같은데.. + + +[1차 시도] 백트래킹 (Backtracking) +──────────────────────────────────────────────────────────────────────────────── +4. 모든 조합을 탐색하되, 조건에 맞지 않으면 가지치기 +5. 같은 숫자를 여러 번 사용할 수 있으므로 재귀 호출 시 같은 인덱스부터 시작 +6. 중복 조합 방지를 위해 start 인덱스 사용 + + result = [] + + def backtrack(start, current, current_sum): + # target을 만족하면 결과에 추가 + if current_sum == target: + result.append(current[:]) # 복사해서 추가 + return + + # 가지치기: 합이 target 초과 + if current_sum > target: + return + + # 조합 탐색 + for i in range(start, len(candidates)): + current.append(candidates[i]) + # 같은 숫자 재사용 가능하므로 i부터 시작 + backtrack(i, current, current_sum + candidates[i]) + current.pop() # 백트래킹: 상태 복원 + + backtrack(0, [], 0) + return result + +7. Example: candidates = [2,3,6,7], target = 7 + + backtrack(0, [], 0) + ├─ 2 추가 → backtrack(0, [2], 2) + │ ├─ 2 추가 → backtrack(0, [2,2], 4) + │ │ ├─ 2 추가 → backtrack(0, [2,2,2], 6) + │ │ │ └─ 2 추가 → backtrack(0, [2,2,2,2], 8) → 8 > 7 return + │ │ └─ 3 추가 → backtrack(1, [2,2,3], 7) → 7 == 7 ✅ [2,2,3] + │ └─ 3 추가 → backtrack(1, [2,3], 5) + │ └─ ... (탐색 계속) + └─ 7 추가 → backtrack(3, [7], 7) → 7 == 7 ✅ [7] + +8. 시간 복잡도: O(N^(T/M)) + - N: candidates 길이 + - T: target 값 + - M: candidates의 최소값 +9. 공간 복잡도: O(T/M) - 재귀 호출 스택 깊이 +""" \ No newline at end of file From 9f8ceb2205b90d11e6b2d01b55a09e1c0f109f1d Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 22 Nov 2025 12:58:54 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20maximum-subarray=20=ED=92=80?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- maximum-subarray/unpo88.py | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 maximum-subarray/unpo88.py diff --git a/maximum-subarray/unpo88.py b/maximum-subarray/unpo88.py new file mode 100644 index 0000000000..70b5067843 --- /dev/null +++ b/maximum-subarray/unpo88.py @@ -0,0 +1,63 @@ +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + current_sum = nums[0] + max_sum = nums[0] + + for i in range(1, len(nums)): + # 현재 값부터 새로 시작 vs 이전 합에 추가 + current_sum = max(nums[i], current_sum + nums[i]) + max_sum = max(max_sum, current_sum) + + return max_sum + + +""" +================================================================================ +풀이 과정 +================================================================================ + +1. 부분 배열 중 가장 큰 합을 찾아야 하는데 어떻게 접근하지? +2. Sliding Window? → 음수/양수가 섞여있어서 윈도우 크기를 언제 조절할지 불명확 +3. 모든 부분배열을 확인? → O(n²)이라 비효율적 +4. 각 위치에서 "현재까지의 최대 부분합"을 추적하면 되지 않을까? + + +[1차 시도] Dynamic Programming +──────────────────────────────────────────────────────────────────────────────── +5. 핵심 아이디어: 각 위치에서 "이 위치를 끝으로 하는" 최대 부분배열의 합 추적 +6. current_sum = "현재 위치를 끝으로 하는 최대 부분합" +7. 매 위치에서 선택: 이전 합에 추가 vs 여기서 새로 시작 + + current_sum = nums[0] # 현재 위치까지의 최대 부분합 + max_sum = nums[0] # 전체 최댓값 + + for i in range(1, len(nums)): + # 이전 합이 양수면 계속, 음수면 버리고 새로 시작 + current_sum = max(nums[i], current_sum + nums[i]) + max_sum = max(max_sum, current_sum) + + return max_sum + +8. Example: nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + + i=0: current=-2, max=-2 + i=1: max(1, -2+1=-1) = 1 (새로 시작), max=1 + i=2: max(-3, 1-3=-2) = -2 (이어감), max=1 + i=3: max(4, -2+4=2) = 4 (새로 시작), max=4 + i=4: max(-1, 4-1=3) = 3 (이어감), max=4 + i=5: max(2, 3+2=5) = 5 (이어감), max=5 + i=6: max(1, 5+1=6) = 6 (이어감), max=6 ✓ + i=7: max(-5, 6-5=1) = 1 (이어감), max=6 + i=8: max(4, 1+4=5) = 5 (이어감), max=6 + + 결과: [4, -1, 2, 1] = 6 + +9. 왜 작동하는가? + - 모든 부분배열은 어딘가에서 끝남 + - 각 위치에서 "여기를 끝으로 하는 최댓값" 추적 + - 이전 합이 음수면 버리는 게 이득 (Greedy한 선택) + - 이전 합이 양수면 현재 값이 음수여도 계속 (예: 4 + (-1) = 3) + +10. 시간 복잡도: O(n) - 배열을 한 번만 순회 +11. 공간 복잡도: O(1) - 변수 2개만 사용 +""" \ No newline at end of file From 1b713c416b7224aa1d70aa39de492410ffc38145 Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sun, 23 Nov 2025 13:12:27 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EC=A4=84=EB=B0=94=EA=BF=88=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- combination-sum/unpo88.py | 2 +- maximum-subarray/unpo88.py | 2 +- number-of-1-bits/unpo88.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/combination-sum/unpo88.py b/combination-sum/unpo88.py index 492444170b..4ed0ac7c7a 100644 --- a/combination-sum/unpo88.py +++ b/combination-sum/unpo88.py @@ -74,4 +74,4 @@ def backtrack(start, current, current_sum): - T: target 값 - M: candidates의 최소값 9. 공간 복잡도: O(T/M) - 재귀 호출 스택 깊이 -""" \ No newline at end of file +""" diff --git a/maximum-subarray/unpo88.py b/maximum-subarray/unpo88.py index 70b5067843..294fe53497 100644 --- a/maximum-subarray/unpo88.py +++ b/maximum-subarray/unpo88.py @@ -60,4 +60,4 @@ def maxSubArray(self, nums: List[int]) -> int: 10. 시간 복잡도: O(n) - 배열을 한 번만 순회 11. 공간 복잡도: O(1) - 변수 2개만 사용 -""" \ No newline at end of file +""" diff --git a/number-of-1-bits/unpo88.py b/number-of-1-bits/unpo88.py index 328d65311f..a6c5fe23b8 100644 --- a/number-of-1-bits/unpo88.py +++ b/number-of-1-bits/unpo88.py @@ -21,4 +21,4 @@ def hammingWeight(self, n: int) -> int: 5. 시간 복잡도: O(log n) - 이진 표현의 비트 수만큼 6. 공간 복잡도: O(log n) - 이진 문자열 생성 -""" \ No newline at end of file +""" From e849508f61a9b0c96df979c802383ee0508ac4d6 Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sun, 23 Nov 2025 13:12:51 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=EC=A4=84=EB=B0=94=EA=BF=88=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- valid-palindrome/unpo88.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valid-palindrome/unpo88.py b/valid-palindrome/unpo88.py index e130ea3f42..b85d8527dd 100644 --- a/valid-palindrome/unpo88.py +++ b/valid-palindrome/unpo88.py @@ -63,4 +63,4 @@ def isPalindrome(self, s: str) -> bool: 4. 시간 복잡도: O(n) - 문자열을 한 번만 순회 5. 공간 복잡도: O(1) - 추가 공간 사용 없음 (포인터 2개만 사용) -""" \ No newline at end of file +""" From fade13e4007c6ab05d40e6ec6b949a60c642bd0d Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sun, 23 Nov 2025 13:23:34 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20decode-ways=20=ED=92=80=EC=9D=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- decode-ways/unpo88.py | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 decode-ways/unpo88.py diff --git a/decode-ways/unpo88.py b/decode-ways/unpo88.py new file mode 100644 index 0000000000..17c6dc9129 --- /dev/null +++ b/decode-ways/unpo88.py @@ -0,0 +1,66 @@ +class Solution: + def numDecodings(self, s: str) -> int: + # 예외 처리: "0"으로 시작하면 불가능 + if not s or s[0] == '0': + return 0 + + n = len(s) + dp = [0] * (n + 1) + + # 초기값 + dp[0] = 1 # 빈 문자열 + dp[1] = 1 # 첫 번째 문자 (이미 "0" 체크함) + + for i in range(2, n + 1): + # 한 자리 숫자 (1~9) + if s[i-1] != '0': + dp[i] += dp[i-1] + + # 두 자리 숫자 (10~26) + two_digit = int(s[i-2:i]) + if 10 <= two_digit <= 26: + dp[i] += dp[i-2] + + return dp[n] + + +""" +================================================================================ +풀이 과정 +================================================================================ +- "226" → 2|2|6 (BBF), 22|6 (VF), 2|26 (BZ) → 3가지 방법 존재 +- DP를 이용해서 각 위치에서 가능한 디코딩 경우의 수를 계산 +- 한 자리(1~9)와 두 자리(10~26) 숫자를 고려하여 누적 + +[1차 시도] +──────────────────────────────────────────────────────────────────────────────── +1. 접근 방법 + - DP 배열 사용: dp[i] = 문자열의 처음부터 i번째까지의 디코딩 경우의 수 + - 각 위치에서 한 자리 숫자(1~9)로 디코딩 가능하면 dp[i-1] 더하기 + - 두 자리 숫자(10~26)로 디코딩 가능하면 dp[i-2] 더하기 + +2. 구현 + n = len(s) + dp = [0] * (n + 1) + + # 초기값 설정 + dp[0] = 1 # 빈 문자열 + dp[1] = 1 # 첫 번째 문자 (0이 아니면 1가지) + + for i in range(2, n + 1): + # 한 자리 숫자 (1~9) + if s[i-1] != '0': + dp[i] += dp[i-1] + + # 두 자리 숫자 (10~26) + two_digit = int(s[i-2:i]) + if 10 <= two_digit <= 26: + dp[i] += dp[i-2] + +3. 예외 처리 + - "0"으로 시작하는 문자열은 디코딩 불가능 → 0 반환 + - 빈 문자열 체크 + +4. 시간 복잡도: O(n) - 문자열을 한 번만 순회 +5. 공간 복잡도: O(n) - DP 배열 사용 +"""