Skip to content

Commit 47e96fa

Browse files
authored
Merge pull request #1501 from KwonNayeon/main
2 parents 82a90a5 + ac9f691 commit 47e96fa

File tree

5 files changed

+178
-30
lines changed

5 files changed

+178
-30
lines changed

clone-graph/KwonNayeon.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,25 @@
2929
- N: 노드의 개수
3030
- dictionary와 재귀 호출 스택 공간
3131
32+
풀이 방법:
33+
- 문제에서 요구하는 것: Deep copy
34+
- Base case: 빈 그래프 처리
35+
- 딕셔너리 생성 후 dfs
36+
- 만약 이미 복사한 노드라면 해당 복사본을 반환함
37+
- 아니라면, 새로운 노드를 생성하여 딕셔너리에 저장
38+
- 이웃 노드의 경우에도 dfs()로 복사본을 만들어서 현재 노드의 neighbors 리스트에 추가함
39+
- 주어진 노드부터 재귀 시작
40+
3241
# Definition for a Node.
3342
class Node:
3443
def __init__(self, val = 0, neighbors = None):
3544
self.val = val
3645
self.neighbors = neighbors if neighbors is not None else []
37-
38-
참고 사항:
39-
- 혼자 풀기 어려워서, 문제와 답을 이해하는 것에 집중했습니다!
4046
"""
4147
class Solution:
4248
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
49+
50+
# 빈 그래프 처리
4351
if not node:
4452
return None
4553

@@ -51,13 +59,11 @@ def dfs(node):
5159

5260
# 새로운 노드 생성
5361
copy = Node(node.val)
54-
dict[node.val] = copy # dictionary에 기록
62+
dict[node.val] = copy
5563

56-
# 각 neighbor에 대해서도 같은 과정 수행
57-
for neighbor in node.neighbors:
58-
copy.neighbors.append(dfs(neighbor))
64+
for neighbor in node.neighbors: # 원본의 각 이웃에 대하여
65+
copy.neighbors.append(dfs(neighbor)) # 그 이웃의 복사본을 만들어서 추가함
5966

6067
return copy
6168

6269
return dfs(node)
63-

longest-common-subsequence/KwonNayeon.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- 1 <= text1.length, text2.length <= 1000
44
- text1 and text2 consist of only lowercase English characters.
55
6+
<Solution 1: DFS, 메모이제이션 활용>
7+
68
Time Complexity: O(m*n)
79
- m은 text1의 길이, n은 text2의 길이
810
- @cache로 중복 계산을 방지하여 각 (i,j) 조합을 한 번만 계산함
@@ -17,7 +19,6 @@
1719
- 다르면: 한쪽만 이동한 경우 중 최댓값 선택
1820
3. base case: 어느 한쪽 문자열 끝에 도달하면 종료
1921
"""
20-
2122
from functools import cache
2223
class Solution:
2324
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
@@ -32,3 +33,48 @@ def dfs(i, j):
3233
return max(dfs(i + 1, j), dfs(i, j + 1))
3334

3435
return dfs(0, 0)
36+
37+
"""
38+
<Solution 2: DP>
39+
40+
Time Complexity: O(n * m)
41+
- 2중 for문으로 모든 dp[i][j] 계산
42+
43+
Space Complexity: O(n * m)
44+
- (n+1) * (m+1) 크기의 DP 테이블 사용
45+
46+
풀이방법:
47+
- 상태 정의: dp[i][j] = text1[:i]와 text2[:j]의 LCS 길이
48+
- Subsequence: 순서만 유지하면 됨
49+
- Substring: 연속적으로 나타나야 함
50+
51+
점화식:
52+
- 문자가 같으면: dp[i][j] = dp[i-1][j-1] + 1
53+
- 문자가 다르면: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
54+
55+
핵심:
56+
- dp 테이블 크기 (n+1) * (m+1): 빈 문자열 케이스 포함
57+
- 최종 답: dp[n][m] (전체 문자열 비교 결과)
58+
59+
노트:
60+
- DP 패턴을 찾아내는 연습하기
61+
- 매트릭스 활용
62+
"""
63+
class Solution:
64+
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
65+
n, m = len(text1), len(text2)
66+
67+
# DP 테이블 초기화
68+
dp = [[0] * (m + 1) for _ in range(n + 1)]
69+
70+
# DP 테이블 채우기
71+
for i in range(1, n + 1):
72+
for j in range(1, m + 1):
73+
if text1[i-1] == text2[j-1]:
74+
# 문자가 같으면: 둘 다 선택 + 이전 결과
75+
dp[i][j] = 1 + dp[i-1][j-1]
76+
else:
77+
# 문자가 다르면: 둘 중 하나 제외하고 최대값
78+
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
79+
80+
return dp[n][m]

longest-repeating-character-replacement/KwonNayeon.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,41 @@
88
- 여기서 n은 문자열의 길이
99
1010
Space Complexity: O(1)
11-
- 추가 변수(left, right, max_length 등)는 상수 개
11+
- 추가 변수(max_length, max_count, start, end 등) 이외의 공간 사용하지 않음
1212
1313
풀이방법:
1414
1. Sliding Window로 구간을 관리
15-
- right 포인터로 구간을 늘리다가
16-
- 변경해야하는 문자 수가 k를 초과하면 left 포인터로 구간을 줄임
15+
- end 포인터로 구간을 늘리다가
16+
- 변경해야하는 문자 수가 k를 초과하면 start 포인터로 구간을 줄임
1717
1818
2. 각 구간에서:
19-
- 가장 많이 등장한 문자로 나머지를 변경
19+
- 나머지 문자를 가장 많이 등장한 문자로 변경
2020
- (구간 길이 - 가장 많이 등장한 문자 수)가 k 이하여야 함
2121
"""
2222
class Solution:
2323
def characterReplacement(self, s: str, k: int) -> int:
24-
counter = {}
25-
left = 0
26-
max_length = 0
27-
28-
for right in range(len(s)):
29-
counter[s[right]] = counter.get(s[right], 0) + 1
30-
31-
curr_length = right - left + 1
24+
from collections import defaultdict
3225

33-
if curr_length - max(counter.values()) > k:
34-
counter[s[left]] -= 1
35-
left += 1
36-
37-
max_length = max(max_length, right - left + 1)
38-
26+
max_length = 0
27+
max_count = 0
28+
start = 0
29+
char_count = defaultdict(int)
30+
31+
for end in range(len(s)):
32+
# 현재 문자의 등장 횟수 증가
33+
char_count[s[end]] += 1
34+
35+
# 윈도우 내 가장 많이 나타난 문자의 등장 횟수 업데이트
36+
max_count = max(max_count, char_count[s[end]])
37+
38+
# 윈도우의 크기 - 가장 많이 나타난 문자의 등장 횟수 = 변경해야 할 문자의 수
39+
# 이 때 변경하는 문자의 종류는 상관없음
40+
# 이 값이 k보다 클 때, 윈도우의 크기를 줄임
41+
if (end - start + 1) - max_count > k:
42+
char_count[s[start]] -= 1
43+
start += 1
44+
45+
# 최대 길이 업데이트
46+
max_length = max(max_length, end - start + 1)
47+
3948
return max_length

palindromic-substrings/KwonNayeon.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
Constraints:
3+
- 1 <= s.length <= 1000
4+
- s consists of lowercase English letters.
5+
6+
<Solution 2>
7+
8+
Time Complexity: O(n²)
9+
- 외부 for문: n번 실행
10+
- 내부 함수 (expand_around_center): 최악의 경우 n번 확장
11+
- 결과: n * n = O(n²)
12+
13+
Space Complexity: O(1)
14+
- 상수 공간만 사용 (count, left, right)
15+
16+
풀이 방법:
17+
1. expand_around_center 헬퍼 함수:
18+
- 주어진 중심에서 양쪽으로 확장하며 팰린드롬의 개수를 셈
19+
- 경계 체크, 문자 일치여부 확인
20+
21+
2. 각 위치에서 두 가지 경우 확인:
22+
- 홀수 길이: expand_around_center(i, i) - 중심이 한 글자
23+
- 짝수 길이: expand_around_center(i, i+1) - 중심이 두 글자
24+
25+
핵심 아이디어:
26+
- 중심에서 바깥으로 확장 (안에서 밖으로)
27+
- 모든 가능한 중심점에서 팰린드롬 탐색
28+
29+
노트:
30+
- dp로도 풀어보기
31+
"""
32+
# Solution 1: Brute-force
33+
class Solution:
34+
def countSubstrings(self, s: str) -> int:
35+
def isPalindromic(start, end):
36+
while start < end:
37+
if s[start] != s[end]:
38+
return False
39+
start, end = start + 1, end - 1
40+
return True
41+
42+
cnt = 0
43+
44+
for i in range(0, len(s)):
45+
for j in range(i, len(s)):
46+
if isPalindromic(i, j):
47+
cnt += 1
48+
return cnt
49+
50+
# Solution 2
51+
class Solution:
52+
def countSubstrings(self, s: str) -> int:
53+
def expand_around_center(left, right):
54+
cnt = 0
55+
56+
while left >= 0 and right < len(s) and s[left] == s[right]:
57+
cnt += 1
58+
left -= 1
59+
right += 1
60+
return cnt
61+
62+
total = 0
63+
64+
for i in range(len(s)):
65+
# 홀수일 때
66+
total += expand_around_center(i, i)
67+
# 짝수일 때
68+
total += expand_around_center(i, i+1)
69+
70+
return total
71+

reverse-bits/KwonNayeon.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
2. 문자열 슬라이싱 [::-1]으로 비트를 뒤집음
1616
3. int(reversed_binary, 2)로 뒤집은 이진수 문자열을 다시 정수로 변환함
1717
"""
18-
1918
class Solution:
2019
def reverseBits(self, n: int) -> int:
2120

@@ -24,12 +23,29 @@ def reverseBits(self, n: int) -> int:
2423
reversed_binary = binary[::-1]
2524

2625
return int(reversed_binary, 2)
26+
27+
# 코드를 간결하게 정리한 버전
28+
class Solution:
29+
def reverseBits(self, n: int) -> int:
30+
31+
return int(format(n, '032b')[::-1], 2)
2732
"""
2833
<Solution 2>
2934
30-
Time Complexity:
35+
Time Complexity: O(1)
36+
- 각 반복에서 비트 연산은 상수 시간이 걸림
3137
32-
Space Complexity:
38+
Space Complexity: O(1)
39+
- 사용되는 변수는 result와 입력값 n밖에 없음
3340
3441
풀이 방법:
42+
- ...
3543
"""
44+
class Solution:
45+
def reverseBits(self, n: int) -> int:
46+
result = 0
47+
for i in range(32):
48+
result <<= 1 # 결과를 왼쪽으로 한 칸 밀고
49+
result |= n & 1 # n의 마지막 비트를 결과에 추가
50+
n >>= 1 # n을 오른쪽으로 한 칸 밀어 다음 비트로 이동
51+
return result

0 commit comments

Comments
 (0)