Skip to content

Commit 480e4b8

Browse files
authored
Merge pull request #484 from haklee/main
[haklee] week 7
2 parents c127512 + 78436f9 commit 480e4b8

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""TC: O(n), SC: O(n)
2+
3+
아이디어:
4+
투포인터로 문자열의 시작, 끝 인덱스를 관리한다. 그리고 문자열 안에 있는 문자를 set으로 관리한다.
5+
- 시작~끝 사이에 모든 문자가 서로 다르면 끝 인덱스를 하나 뒤로 옮긴다.
6+
- 새로운 문자열의 제일 끝에 있는 문자가 혹시 set 안에 있으면 시작 인덱스를 뒤로 옮기는 작업을
7+
해당 문자랑 같은 문자가 나올 때까지 진행한다. 즉, 끝을 고정하고 앞을 계속 옮겨서 새로운 문자열에
8+
있는 모든 문자들이 다른 문자가 되도록 만든다.
9+
- e.g.) abcdefd -> abcdefd
10+
^ ^ ^ ^
11+
s e s e
12+
- 위의 과정을 계속 반복하면서 문자열의 최대 길이 값을 갱신한다.
13+
14+
SC:
15+
- 새로운 문자열의 시작과 끝 인덱스 값을 관리하는 데에 O(1).
16+
- 새로운 문자열 안에 들어있는 문자를 set으로 관리하는 데에 최악의 경우 n개의 문자가 모두 다를때 O(n).
17+
- 최대 문자열 길이를 관리하는 데에 O(1).
18+
- 종합하면 O(n).
19+
20+
TC:
21+
- s, e는 모두 문자열의 인덱스를 나타내므로 s, e값을 아무리 많이 업데이트 해도 각가 문자열 길이보다
22+
많이 업데이트 할 수는 없다. 즉, O(n).
23+
- set에서 특정 문자가 들어있는지 체크하고 set에 문자를 더하거나 제거하는 데에 O(1). 이 작업을 아무리
24+
많이 해도 s, e를 업데이트 하는 회수 만큼 진행하므로 총 O(n).
25+
- 최대 문자열 길이를 업데이트 하는 데에 O(1). 이 또한 아무리 많이 진행해도 O(n).
26+
- 종합하면 O(n).
27+
"""
28+
29+
30+
class Solution:
31+
def lengthOfLongestSubstring(self, string: str) -> int:
32+
if len(string) < 1:
33+
return 0
34+
s = e = 0
35+
letter_set = set([string[0]])
36+
sol = 1
37+
while True:
38+
e += 1
39+
if e == len(string):
40+
break
41+
42+
if string[e] in letter_set:
43+
while True:
44+
letter_set.discard(string[s])
45+
if string[s] == string[e]:
46+
break
47+
s += 1
48+
s += 1
49+
letter_set.add(string[e])
50+
sol = max(len(letter_set), sol)
51+
return sol

number-of-islands/haklee.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""TC: O(m * n), SC: O(m * n)
2+
3+
아이디어:
4+
- 특정 칸이 1일 경우 이와 같은 섬 안에 있는 모든 칸들을 0으로 바꿔주는 `remove_ground` 함수 구현.
5+
- grid 내의 모든 칸들을 돌면서 `remove_ground`가 몇 번 최초 호출(즉, 재귀 호출 아님) 되었는지
6+
세면 전체 섬이 몇 개 있는지 찾을 수 있다.
7+
8+
SC:
9+
- 모든 칸이 전부 1일 경우 remove_ground의 호출 스택 깊이가 m*n이 된다. 즉, O(m * n).
10+
11+
TC:
12+
- 각 노드는 최대 5번씩 접근될 수 있다.
13+
- 이웃한 칸에서 remove_ground 하면서 접근
14+
- 최외곽에서 해당 칸이 1인지 체크하면서 접근
15+
- 접근하고 나서 하는 연산이 O(1).
16+
- 해당 칸의 값이 1인지 체크하는 데에 O(1).
17+
- check_inside 연산을 4번 할 수 있는데 각각 O(1).
18+
- 종합하면 O(1).
19+
- 종합하면 O(m * n).
20+
"""
21+
22+
23+
class Solution:
24+
def numIslands(self, grid: List[List[str]]) -> int:
25+
minx, miny, maxx, maxy = 0, 0, len(grid[0]) - 1, len(grid) - 1
26+
27+
def remove_ground(i, j):
28+
if minx <= j <= maxx and miny <= i <= maxy and grid[i][j] == "1":
29+
grid[i][j] = "0"
30+
remove_ground(i - 1, j)
31+
remove_ground(i + 1, j)
32+
remove_ground(i, j - 1)
33+
remove_ground(i, j + 1)
34+
35+
sol = 0
36+
for i in range(maxy + 1):
37+
for j in range(maxx + 1):
38+
if grid[i][j] == "1":
39+
sol += 1
40+
remove_ground(i, j)
41+
return sol

reverse-linked-list/haklee.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""TC: O(n), SC: O(1)
2+
3+
아이디어:
4+
첫 아이템부터 차례대로 next로 넘어가면서 직전에 보았던 노드를 next에 넣어준다.
5+
6+
SC:
7+
- 직전 노드를 관리한다. O(1).
8+
9+
TC:
10+
- 모든 노드에 대해 직전 노드를 next에 대입한다.
11+
- 그 외에 다른 연산도 O(1). 코드 참조.
12+
- 종합하면 O(n).
13+
"""
14+
15+
16+
# Definition for singly-linked list.
17+
# class ListNode:
18+
# def __init__(self, val=0, next=None):
19+
# self.val = val
20+
# self.next = next
21+
class Solution:
22+
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
23+
prev, cur = None, head
24+
while cur:
25+
next = cur.next
26+
cur.next = prev
27+
prev, cur = cur, next
28+
return prev

set-matrix-zeroes/haklee.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""TC: O(m * n), SC: O(m + n)
2+
3+
아이디어:
4+
모든 칸을 훑으면서 어떤 row, column을 0으로 바꿔줘야 하는지 찾은 다음 0으로 바꾸는 시행을 한다.
5+
6+
SC:
7+
- 바꿔야 하는 column, row를 set으로 관리. 각각 O(m), O(n)이므로 종합하면 O(m + n).
8+
9+
TC:
10+
- 모든 칸을 돌면서 어떤 column과 row를 0으로 바꿔야 하는지 체크한다. O(m * n).
11+
- 0으로 바꿔야 하는 모든 column을 0으로 바꾼다. 모든 column을 다 0으로 바꿔야 할 경우 모든 칸에
12+
접근해서 0을 넣어주어야 하므로 O(m * n).
13+
- row도 column과 똑같이 접근 가능하다. O(m * n).
14+
- 종합하면 O(m * n).
15+
"""
16+
17+
18+
class Solution:
19+
def setZeroes(self, matrix: List[List[int]]) -> None:
20+
"""
21+
Do not return anything, modify matrix in-place instead.
22+
"""
23+
m, n = len(matrix), len(matrix[0])
24+
col_to_change, row_to_change = set(), set()
25+
26+
for i in range(m):
27+
for j in range(n):
28+
if matrix[i][j] == 0:
29+
col_to_change.add(i)
30+
row_to_change.add(j)
31+
32+
for i in col_to_change:
33+
for j in range(n):
34+
matrix[i][j] = 0
35+
36+
for j in row_to_change:
37+
for i in range(m):
38+
matrix[i][j] = 0

unique-paths/haklee.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""TC: O(m + n), SC: O(1)
2+
3+
아이디어:
4+
오른쪽으로 가는 회수 m-1, 아래로 가는 회수 n-1을 섞을 수 있는 방법의 수를 구하면 된다.
5+
즉, m+n-2회의 이동 중 m-1개를 뽑아서 오른쪽으로 가고, 나머지를 아래로 가면 된다.
6+
즉, (m+n-2)C(m-1)을 계산하면 된다.
7+
8+
큰 수 연산이 가능한 언어를 사용하면 nCk = n! / (k! * (n - k)!) 값을 계산하면 된다.
9+
파이썬의 math.comb 함수는 아래와 같이 작동한다.
10+
(ref: https://docs.python.org/ko/3/library/math.html#math.comb)
11+
- math.comb(n, k)을 계산하면 k <= n이면 n! / (k! * (n - k)!)로 평가되고, k > n이면 0으로 평가됩니다.
12+
이 함수를 써서 나온 결과값을 그대로 반환하자.
13+
14+
SC:
15+
- 곱셈, 나눗셈 연산 결과 값 관리. O(1).
16+
- 아주 큰 숫자를 다룰 경우 이렇게 보면 안 될 수도 있지만, 문제 조건을 보아하니 int64 범위 내에서
17+
곱셈과 나눗셈 연산 값들이 모두 처리되는 것으로 보인다. 즉, SC가 그리 중요하지는 않다.
18+
19+
TC:
20+
- (m+n-2)C(m-1) = (m+n-2)! / (m-1)! * (n-1)!
21+
- 각 곱셈 값을 구하는 데에 O(m + n), O(m), O(n). 셋 다 더하면 O(m + n).
22+
- 나눗셈에 O(1).
23+
- 종합하면 O(m + n)
24+
"""
25+
26+
from math import comb
27+
28+
29+
class Solution:
30+
def uniquePaths(self, m: int, n: int) -> int:
31+
return comb(m + n - 2, n - 1)

0 commit comments

Comments
 (0)