-
-
Notifications
You must be signed in to change notification settings - Fork 362
[alphaorderly] WEEK 03 Solutions #2707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
4d5f428
3ac63f5
2536209
612dbbb
a7c2098
79f6f86
3855235
33ab6bc
b1de0e1
f9bf16c
de3390f
b3dc7d7
17669b6
d8b5903
a14205b
b087a32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 각 후보를 순회하며 목표 합까지의 모든 부분합에 대해 가능한 조합을 확장한다. dp 배열의 각 항목이 부분해를 담고 있어 시간은 후보 수와 목표합에 비례한다. 개선 제안: 현재 구현이 적절해 보입니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
|
|
||
| """ | ||
| Time Complexity: O(N^target) | ||
| Space Complexity: O(target) | ||
|
|
||
| Classic backtracking approach. | ||
| - Use a helper function to backtrack and generate all possible combinations. | ||
| """ | ||
| # class Solution: | ||
| # def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| # N = len(candidates) | ||
| # candidates.sort() | ||
|
|
||
| # ans = [] | ||
| # app = [] | ||
|
|
||
| # def backtracking(last: int, left: int) -> None: | ||
| # if left == 0: | ||
| # ans.append(app[:]) | ||
| # return | ||
|
|
||
| # for i in range(last, N): | ||
| # if candidates[i] > left: | ||
| # continue | ||
|
|
||
| # app.append(candidates[i]) | ||
| # backtracking(i, left - candidates[i]) | ||
| # app.pop() | ||
|
|
||
| # backtracking(0, target) | ||
|
|
||
| # return ans | ||
|
|
||
| """ | ||
| Time Complexity: O(N * target) | ||
| Space Complexity: O(target) | ||
|
|
||
| - Use a dynamic programming approach to store the combinations that sum to each target. | ||
| - dp[j] is a list of lists that sum to j. | ||
| """ | ||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| dp = [list() for _ in range(target + 1)] | ||
| dp[0] = [[]] | ||
|
|
||
| for c in candidates: | ||
| for j in range(c, target + 1): | ||
| for partial in dp[j - c]: | ||
| dp[j].append(partial + [c]) | ||
|
|
||
| return dp[-1] |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 두 변수만으로 연속 상태를 유지하며 각 위치의 해석 경우의 수를 계산한다. 시작 조건과 0 처리에 주의해야 한다. 개선 제안: 필요하면 모듈러 연산이나 입력 검증 로직을 추가해 안정성을 높일 수 있습니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(1) | ||
|
|
||
| prev_2 : number of ways to decode the string ending with the previous two characters | ||
| prev_1 : number of ways to decode the string ending with the previous character | ||
| new_prev : number of ways to decode the string ending with the current character | ||
| """ | ||
| class Solution: | ||
| def numDecodings(self, s: str) -> int: | ||
| N = len(s) | ||
|
|
||
| if s[0] == "0": | ||
| return 0 | ||
|
|
||
| if N == 1: | ||
| return 1 | ||
|
|
||
| prev_2 = 1 | ||
| prev_1 = int(1 <= int(s[0] + s[1]) <= 26) + int(s[1] != "0") | ||
|
|
||
| for i in range(2, N): | ||
| new_prev = 0 | ||
| if 1 <= int(s[i - 1] + s[i]) <= 26 and s[i - 1] != "0": | ||
| new_prev += prev_2 | ||
| if s[i] != "0": | ||
| new_prev += prev_1 | ||
|
|
||
| prev_2, prev_1 = prev_1, new_prev | ||
|
|
||
| return prev_1 |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 중첩 부분구조를 제거하고 연속 부분배열의 최댓값을 선형 시간에 구한다. 개선 제안: 음수 배열에 대해서도 안정적으로 동작하는지 테스트를 늘리면 좋습니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
|
|
||
|
|
||
| """ | ||
| Time Complexity: O(NLogN) | ||
| Space Complexity: O(N) ( Reason: Recursive stack pointer for every element ) | ||
|
|
||
| Divide and Conquer Approach | ||
|
|
||
| 1. Divide the array into two halves | ||
| 2. Find the maximum subarray sum in the left half | ||
| 3. Find the maximum subarray sum in the right half | ||
| 4. Find the maximum subarray sum that crosses the midpoint | ||
| 5. Return the maximum of the three sums | ||
| """ | ||
| # class Solution: | ||
| # def maxSubArray(self, nums: List[int]) -> int: | ||
|
|
||
| # def divide(start: int, end: int) -> int: | ||
| # if start >= end: | ||
| # return nums[start] | ||
|
|
||
| # mid = (start + end) // 2 | ||
|
|
||
| # left = divide(start, mid) | ||
| # right = divide(mid + 1, end) | ||
|
|
||
| # left_largest = -float('inf') | ||
| # right_largest = -float('inf') | ||
|
|
||
| # left_acc = 0 | ||
| # right_acc = 0 | ||
|
|
||
| # left_index = mid | ||
| # right_index = mid + 1 | ||
|
|
||
| # while left_index >= start: | ||
| # left_acc += nums[left_index] | ||
| # left_largest = max(left_largest, left_acc) | ||
| # left_index -= 1 | ||
|
|
||
| # while right_index <= end: | ||
| # right_acc += nums[right_index] | ||
| # right_largest = max(right_largest, right_acc) | ||
| # right_index += 1 | ||
|
|
||
| # return max(left_largest + right_largest, left, right) | ||
|
|
||
| # return divide(0, len(nums) - 1) | ||
|
|
||
|
|
||
|
|
||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(1) | ||
|
|
||
| prev : maximum sum of the subarray ending with the previous element | ||
| ans : maximum sum of the subarray | ||
| curr : maximum sum of the subarray ending with the current element | ||
| """ | ||
| class Solution: | ||
| def maxSubArray(self, nums: List[int]) -> int: | ||
| N = len(nums) | ||
| prev = nums[0] | ||
| ans = nums[0] | ||
|
|
||
| for i in range(1, N): | ||
| prev = max(nums[i], prev + nums[i]) | ||
| ans = max(ans, prev) | ||
|
|
||
| return ans |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 루프가 비트가 0이 될 때까지 수행되므로 입력 n의 비트 길이에 비례한다. 개선 제안: n & (n - 1) 기법을 사용하면 1비트를 제거하며 루프 횟수를 줄일 수 있습니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| class Solution: | ||
| def hammingWeight(self, n: int) -> int: | ||
| ans = 0 | ||
| while n: | ||
| ans += n & 1 | ||
| n >>= 1 | ||
| return ans | ||
|
|
||
| # class Solution: | ||
| # def hammingWeight(self, n: int) -> int: | ||
| # return bin(n).count('1') |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 추가 문자열 생성 없이 제자리에서 검사하므로 공간 사용을 줄인다. 개선 제안: isalpha/isnumeric 검사를 더 엄밀히 하여 유효한 문자만 비교하도록 강화할 수 있습니다.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) | ||
|
|
||
| - Process string with lower() and isalnum() to remove non-alphanumeric characters | ||
| - Check if the processed string is a palindrome by comparing it to its reverse | ||
| """ | ||
| # class Solution: | ||
| # def isPalindrome(self, s: str) -> bool: | ||
| # processed = "".join(ch.lower() for ch in s if ch.isalnum()) | ||
|
|
||
| # return processed == processed[::-1] | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(1) | ||
|
|
||
| - Use two pointers to check if the string is a palindrome | ||
| - Skip non-alphanumeric characters | ||
| - Compare characters from both ends towards the center | ||
| """ | ||
| class Solution: | ||
| def isPalindrome(self, s: str) -> bool: | ||
| N = len(s) | ||
|
|
||
| left, right = 0, N - 1 | ||
|
|
||
| while True: | ||
| while not s[left].isalnum() and left < right: | ||
| left += 1 | ||
| while not s[right].isalnum() and left < right: | ||
| right -= 1 | ||
|
|
||
| if left >= right: | ||
| break | ||
|
|
||
| if s[left].lower() == s[right].lower(): | ||
| left, right = left + 1, right - 1 | ||
| else: | ||
| return False | ||
|
|
||
| return True | ||
|
Comment on lines
+22
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 바깥 while True: + break 구조가 동작은 완벽한데, 조건을 위로 올리면 흐름이 더 읽기 쉬워질 것 같아요. class Solution:
def isPalindrome(self, s: str) -> bool:
left, right = 0, len(s) - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left, right = left + 1, right - 1
return True
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요, 차라리 얼리 리턴을 하는게 나았긴 하네요 |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
답 둘 다 정확한 풀이네요. 👍
한 가지 궁금한 게, 실제 제출은 DP로 하고 백트래킹을 주석으로 남기신 이유가 있을까요?
이 문제(모든 조합 나열)는 출력 자체가 지수적으로 커질 수 있어서, DP 쪽에 적힌
O(N*target) / O(target)이 실제보다 낙관적으로 보여요.dp[j].append(partial + [c])부분이 매번 리스트를 통째로 복사하고,dp[0..target]각 칸에 중간 조합들을 다 들고 있어서 시간/공간 모두 그 표기보단 커질 것 같아요. 오히려 주석 처리된 백트래킹이path하나를push/pop으로 재활용해서 메모리 면에선 더 유리해 보이는데, 혹시 DP를 고른 특별한 이유가 있으면 궁금합니다!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞아요, 리스트를 통으로 복사하는 과정에서 오버헤드가 생깁니다!
다만 백트래킹은 target의 값이 커지면 커질수록 exponential하게 시간복잡도가 커지기 때문에 사실상 큰 값에서는 사용하기 어려울것이 있고, 그에 비해서 trade-off를 고려했을때 DP가 좀 더 낫다고 판단했어요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한 백트래킹과 달리 재귀를 쓰지 않으니 재귀 호출에 의한 스택 오버헤드도 피할수 있겠죠
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재귀 스택 회피는 고려할만한 트레이드오프인 것 같네요 파이썬은 기본 재귀 한도가 1000으로 알고있어 target이 아주 크면 백트래킹이 RecursionError를 낼 수도 있으니, 이건 DP가 분명히 이득 보는 지점으로 생각됩니다. 🥇
다만 두 가지만 덧붙이면요. (1) 메모리는 재귀 스택 O(depth)보다 DP가 지는 짐(중간 조합 전부 저장 + partial 복사)이 더 커서, 총량은 백트래킹이 가볍습니다. (2) 시간은 결과 조합 수가 지수적이라 둘 다 같은 지수 하한이에요 — 이 부분엔 DP도 이득이 없어요. 그래서 강점이 서로 다른 쪽(DP=스택 안정, 백트래킹=메모리/상수)이라고 보는 게 정확할 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.