-
-
Notifications
You must be signed in to change notification settings - Fork 195
[haklee] week 4 #432
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
[haklee] week 4 #432
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
"""TC: O(n), SC: O(n) | ||
|
||
아이디어: | ||
- nums 안에는 여러 consecutive sequence(이하 cs)가 존재할 것이다(길이 1인 것까지 포함). | ||
- 이 cs는 모두 제일 앞 숫자를 가지고 있다. | ||
- 제일 앞 숫자는 그 숫자 바로 앞에 숫자가 없다는 특징을 가지고 있다. | ||
- 즉, i가 제일 앞 숫자라면 i-1은 nums 안에 없다. | ||
- nums에서 제일 앞 숫자를 찾은 다음, 이 숫자부터 뒤로 이어지는 cs를 찾아서 길이를 구할 수 있다. | ||
- cs의 길이 중 제일 긴 것을 찾아서 리턴하면 된다. | ||
|
||
SC: | ||
- set(nums)에서 O(n). | ||
- 위 set에서 모든 아이템을 돌면서 i-1이 set 안에 포함되지 않는 i를 찾아서 리스트로 만들때 O(n). | ||
- 총 O(n). | ||
|
||
TC: | ||
- set(nums)에서 O(n). | ||
- 위 set에서 모든 아이템을 돌면서 i-1이 set 안에 포함되지 않는 i를 찾는 데에 O(n). | ||
- 각 cs의 제일 앞 숫자부터 이어지는 숫자들이 set 안에 있는지 체크하는 데에 총 O(n). | ||
- 총 O(n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def longestConsecutive(self, nums: List[int]) -> int: | ||
s = set(nums) # TC:O(n), SC:O(n) | ||
seq_start_candidate = [i for i in s if i - 1 not in s] # TC:O(n), SC:O(n) | ||
sol = 0 | ||
|
||
# 아래의 for문 내에서는 s에 속한 아이템에 한 번씩 접근한다. TC:O(n) | ||
for i in seq_start_candidate: | ||
seq_len = 1 | ||
while i + seq_len in s: | ||
seq_len += 1 | ||
sol = max(seq_len, sol) | ||
return sol |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"""TC: O(n), SC: O(1) | ||
|
||
※ 코드를 보고 대충 뭘 했는지 감을 잡은 다음에 아래의 아이디어에서 p, n 구간이 나오는 부분만 보면 | ||
좀 더 빠른 이해가 가능하다. | ||
|
||
아이디어: | ||
- 곱에 0이 섞이면 아무리 많은 수를 곱해도 결과는 0이다. | ||
- 0이 등장하지 않는 어떤 subarray가 주어졌다고 하자. | ||
- 여기에 음수가 짝수 번 등장하면 모든 숫자를 곱한 것이 가장 큰 곱이 된다. | ||
- 음수가 홀수 번, 총 r번 등장한다고 해보자. 이 배열을 다음과 같이 구조화할 수 있다. | ||
- 양수를 p, 음수를 n이라고 표현하자. | ||
- 이 배열은 [p, ..., p, n, p, ... p, n, ..., n, p, ..., p] 꼴로 표현이 가능하다. | ||
이때, n은 총 r번 등장하고, n은 p가 여러 번(0번도 가능) 등장하는 묶음 사이에 존재한다. | ||
- p가 여러 번(0번도 가능) 등장하는 것을 P라고 묶어서 표현하면 아래와 같이 볼 수 있다. | ||
- [(P), n, (P), n, ..., n, (P), n, (P)] | ||
- n을 짝수 번 곱해야 양수가 나온다. 연속된 값을 최대한 많이 곱하려고 하므로, 한쪽 끝에 | ||
등장하는 n을 뺀 나머지 n들을 곱하는 것이 가장 좋은 전략이다. | ||
- 위의 전략에 따라 배열의 숫자를 곱하려고 하면 다음 둘 중 하나가 가장 큰 숫자다. | ||
- [(P), n, (P), n, ..., n, (P), n, (P)] | ||
└───────────────────────┘ | ||
이 구간의 숫자들을 모두 곱함 | ||
- [(P), n, (P), n, ..., n, (P), n, (P)] | ||
└───────────────────────┘ | ||
이 구간의 숫자들을 모두 곱함 | ||
- 즉, 앞에서부터 숫자를 계속 곱하면서 max값을 찾은 것, 혹은 뒤에서부터 숫자를 계속 | ||
곱하면서 max값을 찾은 것, 둘 중 하나가 위의 구간에서의 최대 곱셈 값이 된다. | ||
- 우리에게 주어진 전체 array는 다음과 같이 표현 가능하다. | ||
- 0이 등장하지 않는 길이 1 이상의 subarray를 (S), 0으로만 이루어진 길이 0 이상의 subarray를 | ||
(0)이라고 하면 아래와 같이 표현이 가능하다. | ||
- [(0), (S), (0), (S), ..., (S), (0), (S), (0)] | ||
- 전체 array에서 최대 subarray 곱은 0이거나, 혹은 위의 각 S에서의 최대 곱들 중 최대 값이다. | ||
- 위의 아이디어를 활용하면 다음의 방식으로 최대 subarray의 곱을 찾을 수 있다. | ||
- nums의 앞에서부터 숫자를 하나씩 곱해가면서 최대 곱을 찾음. 단, 중간에 0이 나와서 최대 곱 | ||
값이 0이 되었을 경우 이를 다시 1로 바꿔줘서 위의 아이디어를 적용할 수 있도록 세팅. | ||
- 똑같은 작업을 nums의 뒤에서부터 숫자를 하나씩 곱해가면서 진행함. | ||
|
||
|
||
SC: | ||
- solution을 저장하는 데에 O(1). | ||
- 곱셈 값을 저장하는 데에 O(1). | ||
- 총 O(1). | ||
|
||
TC: | ||
- 리스트를 앞에서부터 순회하면서 곱/max 연산. O(n). | ||
- 리스트를 뒤에서부터 순회하면서 곱/max 연산. O(n). | ||
- 총 O(n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def maxProduct(self, nums: List[int]) -> int: | ||
sol = nums[0] | ||
p = 1 | ||
for i in nums: | ||
if p == 0: | ||
p = 1 | ||
p *= i | ||
sol = max(p, sol) | ||
p = 1 | ||
for i in reversed(nums): | ||
if p == 0: | ||
p = 1 | ||
p *= i | ||
sol = max(p, sol) | ||
return sol |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
"""TC: O(n), SC: O(n) | ||
|
||
아이디어: | ||
- [0, ..., n]에서 한 숫자만 빠져있는 상황. | ||
- [0, ..., n]을 set으로 만든 다음 특정 숫자가 이 set에 있는지 체크하면 된다. | ||
- 그런데 그렇게 구현하나 위 set에서 set(nums)를 빼고 남은 숫자를 취하나 최악의 경우 | ||
같은 성능이 나올테니 더 코드가 짧아지도록 후자의 방식으로 구현해보자. | ||
|
||
|
||
SC: | ||
- [0, ..., n]으로 set을 만드는 데에 O(n). | ||
- set(nums)에서 O(n). | ||
- 총 O(n). | ||
|
||
TC: | ||
- [0, ..., n]으로 set을 만드는 데에 O(n). | ||
- set(nums)에서 O(n). | ||
- set에 difference(아래 코드에서는 `-`)를 하는 데에 O(n). | ||
- set에 pop을 하는 데에 O(1). | ||
- 총 O(n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def missingNumber(self, nums: List[int]) -> int: | ||
return (set(range(len(nums) + 1)) - set(nums)).pop() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
"""TC: O(n), SC: O(n) | ||
|
||
아이디어: | ||
문자열을 보고 숫자 혹은 알파벳인 문자만 뽑아서 새 문자열을 만들고, 대문자는 소문자로 바꾼다. | ||
구현이 어렵지는 않지만 귀찮을 수 있는데, python에는 위 과정을 `isalnum()`, `lower()` 함수로 | ||
쉽게 처리할 수 있다. 마지막으로 새로 만든 문자열을 뒤집어서 원래 문자열과 같은지 확인하면 된다. | ||
|
||
|
||
SC: | ||
- 문자열을 필요한 문자만 남기는 과정에서 O(n). | ||
- 문자열을 뒤집어서 저장. O(n). | ||
- 즉, O(n). | ||
|
||
TC: | ||
- 문자열을 필요한 문자만 남기는 과정에서 O(n). | ||
- 문자열 뒤집기. O(n). | ||
- 새로 만든 문자열과 뒤집은 문자열 palindrome 체크. O(n). | ||
- 즉, O(n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def isPalindrome(self, s: str) -> bool: | ||
return (l := [c.lower() for c in s if c.isalnum()]) == l[::-1] | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
"""TC: O(m * n * (4^l)), SC: O(m * n) | ||
|
||
아이디어: | ||
- 격자판의 각 칸을 노드로, 이웃한 칸들의 관계를 엣지로 생각하면 격자판을 그래프로 볼 수 있다. | ||
- 위 그래프에서 dfs를 돌린다. | ||
- 이때, 기존에 방문했던 노드를 재방문하지 않도록 visited라는 2차원 배열을 같이 관리해주자. | ||
|
||
SC: | ||
- visited 배열에서 O(m * n) | ||
- 호출 스택은 찾고자 하는 단어의 길이 l, 즉, O(l). | ||
- 그런데 l이 격자 전체 칸 개수보다 클 수 없으므로 무시 가능. | ||
- 총 O(m * n). | ||
Comment on lines
+11
to
+12
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 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 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 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 commentThe reason will be displayed to describe this comment to others. Learn more. @haklee @obzva 제가 오늘 알고달레 글을 정정하려다가 이 거에 대해서 다시 생각해보게 되었는데요. 우리가 너무 호출 스택의 크기에만 초점을 맞춰서 논의한 게 아닌가 하는 생각이 들었습니다. 사실 이 답안의 공간 복잡도는 다음 2가지 측면에서 분석이 되야할 것 같아요.
제가 재귀 함수의 호출 스택이 차지하는 메모리를 @haklee 님과 다르게
결국 스택 깊이의 상하선을 결정짓는 것은 제가 over thinking 했을까요? 😅 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. @DaleSeo 늦은 확인 및 답변 죄송합니다..! ㅠㅠ |
||
|
||
TC: | ||
- visited 배열 세팅, O(m * n) | ||
- dfs, 최악의 경우 | ||
- 단어를 찾는 시도를 하는 데에 4^l 만큼의 탐색이 걸림 | ||
- 그런데 답을 찾을 수 있는 시작 칸을 하필 제일 마지막으로 탐색한 경우 위의 시도를 m*n번 해야함 | ||
- 즉, O(m * n * (4^l)) | ||
- 총 O(m * n * (4^l)) | ||
""" | ||
|
||
|
||
class Solution: | ||
def exist(self, board: List[List[str]], word: str) -> bool: | ||
r, c = len(board), len(board[0]) | ||
visited = [[False for _ in range(c)] for _ in range(r)] | ||
|
||
def search(ind: int, pos: tuple[int, int]) -> bool: | ||
if ind == len(word): | ||
# 찾는 데에 성공. | ||
return True | ||
|
||
if not (0 <= pos[0] < r and 0 <= pos[1] < c): | ||
# 격자판을 벗어남. | ||
return False | ||
|
||
if visited[pos[0]][pos[1]]: | ||
# 이미 방문함. | ||
return False | ||
|
||
if word[ind] != board[pos[0]][pos[1]]: | ||
# 글자가 안 맞음. | ||
return False | ||
|
||
visited[pos[0]][pos[1]] = True # 방문한 것으로 체크 | ||
|
||
found = ( | ||
search(ind + 1, (pos[0] - 1, pos[1])) # 상 | ||
or search(ind + 1, (pos[0] + 1, pos[1])) # 하 | ||
or search(ind + 1, (pos[0], pos[1] - 1)) # 좌 | ||
or search(ind + 1, (pos[0], pos[1] + 1)) # 우 | ||
) # 다음 글자 찾기 | ||
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. 오 이부분 제가 딱 원하던 방식이었는데 이렇게 하면 되는군요 ...
for (let i = 0; i < 4; i++) {
const [dh, dw] = directives[i];
if (dfs(h + dh, w + dw, index + 1)) {
return true;
}
... 취향 문제겠지만, 제 풀이중에 이 부분이 정말 마음에 들지 않았는데 덕분에 아래처럼 수정 할 수 있을것같습니다! const found = (
dfs(h + 1, w, index + 1) ||
dfs(h - 1, w, index + 1) ||
dfs(h, w + 1, index + 1) ||
dfs(h, w - 1, index + 1)
); |
||
|
||
# 앞에서 못 찾았을 경우에는 방문을 해제해야 한다. | ||
# 찾은 경우에는 방문을 해제하든 말든 상관 없음. | ||
visited[pos[0]][pos[1]] = False | ||
|
||
return found | ||
|
||
return any(search(0, (i, j)) for i in range(r) for j in range(c)) |
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.
뭡니까 이거 ㅋㅋㅋ 약 오르네요 ㅎㅎㅎ