Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions clone-graph/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: DFS
  • 설명: 이 코드는 그래프의 모든 노드를 재귀적으로 탐색하며 복사하는 DFS 패턴을 사용합니다. memo 딕셔너리를 통해 이미 방문한 노드를 재사용하여 무한루프를 방지합니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from typing import Optional

# 7기 풀이
# 시간 복잡도: O(V + E)
# - 모든 노드의 수와 모든 엣지의 수만큼 탐방하므로 그만큼의 시간이 걸림(V: 노드 수, E: 엣지 수)
# 공간 복잡도: O(V)
# - memo에 모든 노드를 복사해서 추가(V: 노드의 갯수)
class Solution:
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
memo = {}

def copy_graph(node):
if not node:
# 더이상 복사할 노드가 없다면 None을 return
return None

if node.val in memo:
# node의 값이 memo에 있다면 memo에 있는 값을 return
return memo[node.val]

# Node의 복사를 위해 새 노드 생성 및 node.val 값을 초기값으로 입력
res_node = Node(node.val)
memo[node.val] = res_node # memo에 새로만든 객체를 value로 하여 저장(key는 node.val 값으로)

# neighbors도 복사, 이 때 copy_graph를 재귀로 호출하여 모든 neighbors들을 복사하도록 한다.
res_node.neighbors = [
copy_graph(neighbor) for neighbor in node.neighbors
]

# res_node를 return
return res_node

return copy_graph(node)# 다음과 같이 파이썬의 built-in 함수인 deepcopy를 사용하면 바로 문제가 풀리기도 했다.


# 정리 목적
# 다음과 같이 파이썬의 built-in 함수인 deepcopy를 사용하면 바로 문제가 풀리기도 했다.
# 실제 deepcopy 구현이 memo를 이용하며
# copier의 deepcopy를 호출하여 재귀와 비슷하게 구현했다는 것을 확인할 수 있었다
# ref: https://github.com/python/cpython/blob/main/Lib/copy.py
from copy import deepcopy


class Solution:
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
return deepcopy(node)
26 changes: 26 additions & 0 deletions longest-common-subsequence/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Dynamic Programming
  • 설명: 이 코드는 두 문자열의 최장 공통 부분 수열을 찾기 위해 DP 테이블을 활용하여 이전 계산 결과를 저장하며 최적 해를 도출하는 방식입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 7기 풀이
# 시간 복잡도: O(n * m)
# - text1의 길이 n과 text2의 길이 m만큼 순회하며 계산함
# 공간 복잡도: O(n * m)
# - text1의 길이 n과 text2의 길이 m만큼의 2차 배열을 만들어 DP 계산을 하기 때문
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
dp = [
[0 for _ in range(len(text2) + 1)]
for _ in range(len(text1) + 1)
]

for i in range(1, len(text1) + 1):
for j in range(1, len(text2) + 1):
if text1[i - 1] == text2[j - 1]:
# text1의 i번째 문자와 text2의 j번째 문자가 같다면
# 이 문자는 공통 subsequence에 포함될 수 있으므로
# 두 문자를 제외한 나머지(dp[i-1][j-1])에 1을 더한 값이 현재의 LCS 길이가 된다
dp[i][j] = dp[i - 1][j - 1] + 1
else:
# 같지 않다면 둘 중 하나를 제외했을 때의 LCS 중 더 큰 값을 가져온다
# dp[i][j-1]: text2의 j번째 문자를 제외한 경우
# dp[i-1][j]: text1의 i번째 문자를 제외한 경우
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j])

return dp[-1][-1]
32 changes: 32 additions & 0 deletions longest-repeating-character-replacement/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Sliding Window
  • 설명: 이 코드는 슬라이딩 윈도우 기법을 활용하여 문자열 내에서 조건에 맞는 가장 긴 부분 문자열을 찾는 문제를 해결합니다. 윈도우 크기를 조절하며 조건을 만족하는지 체크하는 방식이 핵심입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from collections import defaultdict


# 7기 풀이
# 시간 복잡도: O(n)
# - right는 매 루프마다 전진하고 left도 최대 n번 전진하므로 합쳐서 O(n)
# 공간 복잡도: O(1)
# - s에 있는 문자들의 개수만큼 공간 복잡도가 늘어나겠지만 모두 대문자인 알파벳만이 key로 들어오므로 최대 26개
class Solution:
# 기본 아이디어: 슬라이딩 윈도우를 사용하면서
# 윈도우 내에 가장 많이 있는 문자의 개수와 k값을 더한 값이 윈도우를 초과하는지 아닌지를 확인
def characterReplacement(self, s: str, k: int) -> int:
left = 0 # 윈도우 왼쪽 인덱스 값
max_len = 0 # 문제의 답(변경 시 가장 긴 substring 길이)
char_dict = defaultdict(int) # 윈도우 내의 각 문자들 개수를 확인하기 위한 dict
max_char_cnt = 0 # 윈도우 내에 가장 많은 문자의 개수 그 자체(dict의 value() 메서드를 매번 호출하지 않게 하기 위함)

for right in range(len(s)): # 윈도우 오른쪽 인덱스 값
char_dict[s[right]] += 1 # 오른쪽 인덱스에 해당하는 문자(예: A)에 대한 개수를 하나 올림
max_char_cnt = max(max_char_cnt, char_dict[s[right]]) # 현재 윈도우에서 가장 많은 문자의 개수를 업데이트

if max_char_cnt + k >= right - left + 1:
# 가장 많은 문자열 사이에 있는 다른 문자들의 개수가 k보다 작으면 변경 가능
# -> 윈도우 내에서 가장 긴 repeating substring을 만들 수 있음
max_len = max(max_len, right - left + 1)
else:
# 만들 수 없는 경우에는 기존 left의 문자를 char_dict로부터 하나 줄이고
# left를 하나 옮긴다(새로운 윈도우를 만든다는 의미)
char_dict[s[left]] -= 1
left += 1

return max_len
32 changes: 32 additions & 0 deletions palindromic-substrings/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Two Pointers, Sliding Window
  • 설명: 중심 확장법은 양쪽 포인터를 이용해 펠린드롬을 찾는 방식으로, 두 포인터를 활용하는 패턴과 슬라이딩 윈도우 개념이 적용됩니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 7기 풀이
# 시간 복잡도: O(n ** 2)
# - for문으로 문자의 중심을 탐색 * while문으로 양쪽으로 뻗어나가며 문자열 탐색
# 공간 복잡도: O(1)
# - 변수 몇 개만 사용
class Solution:
# 해당 문제의 기본 아이디어: 중심 확장법을 이용하여 펠린드롬 찾기
def countSubstrings(self, s: str) -> int:
res = 0

# 펠린드롬의 길이가 짝수일 수도 홀수일 수도 있기 때문에
# 2 * len(s)를 돌고 나눈 수 몫을 이용해서 중심을 먼저 잡는다
for idx in range(2 * len(s) - 1):
# 펠린드롬 길이가 짝수일 때는 left와 right의 값이 1 차이나지만
# 홀수일 때는 left == right 이다.
left = idx // 2
right = (idx + 1) // 2
delta = 0

while (
0 <= left - delta # 왼쪽으로 확장했을 때의 index와
and right + delta < len(s) # 오른쪽으로 확장했을 때의 index가 모두 s길이 범위 내에 있어야 함
):
if s[left - delta] == s[right + delta]: # 두 개가 같은 경우에는 펠린드롬
res += 1 # 결과 값을 하나 추가하고
delta += 1 # 확장을 위한 값을 1 올려준다
else:
# 확장했을 때의 양 쪽 문자가 다른 경우에는 펠린드롬이 아니므로 break하고
# 다음 루프에서 새로운 중심을 잡은 후 다시 펠린드롬을 찾는다.
break

return res
25 changes: 25 additions & 0 deletions reverse-bits/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Bit Manipulation
  • 설명: 이 코드는 비트 연산을 활용하여 정수의 비트 순서를 뒤집는 문제로, 비트 조작을 통해 효율적으로 수행합니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 7기 풀이
# 시간 복잡도: O(1)
# - 해당 문제는 조건 자체가 32자릿수로 되어 있어 최대 32번까지만 계산
# 공간 복잡도: O(1)
# - result, idx와 같은 변수만 사용
class Solution:
def reverseBits(self, n: int) -> int:
result = 0
idx = 0 # 자릿수 계산을 위한 앵커

while n > 0: # n이 0이 되기 전까지 돌린다.
remainder = n % 2 # 수를 2로 나눴을 때의 나머지가 해당 자리수에서의 2진수 값 (0 or 1)
quotient = n // 2 # 몫은 다음 루프의 피젯수로

if remainder:
# 나머지가 있는 경우에는 해당 2진수 자리수에 값이 있다.
# reversed한 값을 계산해야 하기 때문에 32 - idx - 1의 자릿수로 변환
# (문제 조건 상 32자릿수가 기준)
# 변환한 후 result에 값을 더해준다.
result += 2 ** (32 - idx - 1)

n = quotient
idx += 1 # 자릿수를 하나 올린다

return result
33 changes: 33 additions & 0 deletions set-matrix-zeroes/liza0525.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Hash Map / Hash Set, Matrix
  • 설명: 이 코드는 0이 있는 행과 열을 각각 저장하기 위해 set을 사용하며, 이를 기반으로 행과 열을 0으로 변경하는 방식으로 문제를 해결합니다. 주로 Hash Set을 활용하는 패턴입니다.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 문제는 7주차 문제였습니다! 저번주에 누락해서 못올려서 같이 올립니다 ㅠㅠ
리뷰해주시는 분께 인지 차원으로 말씀드려요~!

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 7기 풀이
# 시간 복잡도: O(m * n)
# - matrix 크기 만큼 시간 복잡도가 듦
# 공간 복잡도: O(m + n)
# - 가로, 세로에서 각각 대상 인덱스를 저장
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
len_i = len(matrix)
len_j = len(matrix[0])

# 0이 발견된 행/열 인덱스를 각각 저장하는 set
target_i_set, target_j_set = set(), set()

for i in range(len_i):
for j in range(len_j):
if matrix[i][j] != 0:
continue

# matrix[i][j]가 0인 경우에는 인덱스를 저장한다.
target_i_set.add(i)
target_j_set.add(j)


for i in range(len_i):
for j in range(len_j):
if matrix[i][j] == 0:
continue
if i in target_i_set or j in target_j_set:
# i, j 인덱스가 set에 있는 경우에는 0으로 변경한다.
matrix[i][j] = 0
Loading