Skip to content

Commit 80ad9ab

Browse files
authored
Merge branch 'DaleStudy:main' into main
2 parents 8ce714b + 67121cd commit 80ad9ab

File tree

18 files changed

+539
-0
lines changed

18 files changed

+539
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package leetcode_study
2+
3+
/*
4+
* Graph Node 볡사 문제
5+
* queueλ₯Ό μ‚¬μš©ν•΄ 문제 ν•΄κ²°
6+
* μƒˆλ‘œ μƒμ„±ν•œ Node에 λŒ€ν•΄ 방문처리λ₯Ό ν•˜μ§€ μ•Šμ„ 경우 λ¬΄ν•œ Loop에 λΉ μ§€κ²Œλ¨ -> 각 λ…Έλ“œκ°€ μ–‘λ°©ν–₯을 가리킀고 있기 λ•Œλ¬Έ.
7+
* λ”°λΌμ„œ, Map 자료ꡬ쑰λ₯Ό μ‚¬μš©ν•΄ Map<κΈ°μ‘΄ Node, 볡사 Node>의 λͺ¨μŠ΅μœΌλ‘œ Key-Value 쌍으둜 방문처리λ₯Ό ν‘œν˜„
8+
*
9+
* μ‹œκ°„ λ³΅μž‘λ„: O(n)
10+
* -> graph의 Nodeλ₯Ό ν•œ λ²ˆμ”© λ°©λ¬Έν•˜μ—¬ λ³΅μ‚¬ν•˜λŠ” κ³Όμ •: O(n)
11+
* 곡간 λ³΅μž‘λ„:
12+
* -> λ³΅μ‚¬λœ Nodeλ₯Ό λ§€ν•‘ν•˜λŠ” Map 자료ꡬ쑰의 크기: O(n)
13+
* -> BFSλ₯Ό μ‚¬μš©ν•œ queue size: O(n)
14+
* -> μƒˆλ‘œ μƒμ„±λœ Node의 neighbor list: O(n)
15+
* */
16+
fun cloneGraph(node: Node?): Node? {
17+
if (node == null) return null
18+
if (node.neighbors.isEmpty()) return Node(1)
19+
20+
// Map< κΈ°μ‘΄ Node, 볡사 Node>
21+
val nodeMap = mutableMapOf<Node, Node>()
22+
23+
val queue = ArrayDeque<Node>()
24+
queue.add(node)
25+
nodeMap[node] = Node(node.`val`)
26+
27+
while (queue.isNotEmpty()) {
28+
val current = queue.removeFirst()
29+
val clonedNode = nodeMap[current]!! // ν˜„μž¬ λ…Έλ“œμ˜ 볡제본
30+
31+
for (neighbor in current.neighbors) {
32+
if (neighbor == null) continue
33+
34+
// ν•΄λ‹Ή 이웃이 아직 λ³΅μ‚¬λ˜μ§€ μ•Šμ•˜λ‹€λ©΄ λ³΅μ‚¬ν•˜μ—¬ 맡에 μ €μž₯ν•˜κ³  큐에 μΆ”κ°€
35+
if (!nodeMap.containsKey(neighbor)) {
36+
nodeMap[neighbor] = Node(neighbor.`val`)
37+
queue.add(neighbor)
38+
}
39+
40+
// 볡제된 ν˜„μž¬ λ…Έλ“œμ˜ 이웃 λ¦¬μŠ€νŠΈμ— 볡제된 이웃 λ…Έλ“œλ₯Ό μΆ”κ°€
41+
// μ–‘λ°©ν–₯을 λ”°μ§ˆ ν•„μš” 없이 λ‚΄λΆ€ neighbor node list에 λͺ¨λ“  λ…Έλ“œκ°€ 있음
42+
clonedNode.neighbors.add(nodeMap[neighbor])
43+
}
44+
}
45+
return nodeMap[node]
46+
}
47+

β€Žclone-graph/dusunax.pyβ€Ž

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'''
2+
# 133. Clone Graph
3+
This is the problem for copying nodes, which helps you understand the concept of referencing a node & copying the node (creating a new node from the existing one).
4+
5+
πŸ‘‰ Perform recursion DFS with the correct escape condition and handling of NoneType.
6+
'''
7+
8+
"""
9+
# Definition for a Node.
10+
class Node:
11+
def __init__(self, val = 0, neighbors = None):
12+
self.val = val
13+
self.neighbors = neighbors if neighbors is not None else []
14+
"""
15+
from typing import Optional
16+
class Solution:
17+
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
18+
if node is None:
19+
return None
20+
21+
visited = {}
22+
23+
def DFS(currNode):
24+
if currNode.val in visited:
25+
return visited[currNode.val]
26+
27+
copiedNode = Node(currNode.val)
28+
visited[currNode.val] = copiedNode
29+
30+
for neighbor in currNode.neighbors:
31+
copiedNode.neighbors.append(DFS(neighbor))
32+
33+
return copiedNode
34+
35+
return DFS(node)

β€Žclone-graph/pmjuu.pyβ€Ž

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'''
2+
μ‹œκ°„ λ³΅μž‘λ„: O(V + E)
3+
- κ·Έλž˜ν”„μ˜ λͺ¨λ“  λ…Έλ“œλ₯Ό ν•œ λ²ˆμ”© λ°©λ¬Έν•΄μ•Ό ν•˜λ―€λ‘œ O(V)
4+
- 각 λ…Έλ“œμ˜ λͺ¨λ“  간선을 ν•œ λ²ˆμ”© 탐색해야 ν•˜λ―€λ‘œ O(E)
5+
- λ”°λΌμ„œ 전체 μ‹œκ°„ λ³΅μž‘λ„λŠ” O(V + E)
6+
7+
곡간 λ³΅μž‘λ„: O(V + E)
8+
- 클둠 λ…Έλ“œλ₯Ό μ €μž₯ν•˜λŠ” λ”•μ…”λ„ˆλ¦¬(clones): O(V)
9+
- BFS 탐색을 μœ„ν•œ 큐(queue): O(V)
10+
- 볡제된 κ·Έλž˜ν”„μ˜ λ…Έλ“œμ™€ κ°„μ„  μ €μž₯ 곡간: O(V + E)
11+
'''
12+
13+
from typing import Optional
14+
from collections import deque
15+
# Definition for a Node.
16+
class Node:
17+
def __init__(self, val = 0, neighbors = None):
18+
self.val = val
19+
self.neighbors = neighbors if neighbors is not None else []
20+
21+
class Solution:
22+
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
23+
if not node:
24+
return None
25+
26+
clones = { node.val: Node(node.val) }
27+
queue = deque([node])
28+
29+
while queue:
30+
current_node = queue.popleft()
31+
32+
for neighbor in current_node.neighbors:
33+
# add neighbors
34+
if neighbor.val not in clones.keys():
35+
queue.append(neighbor)
36+
clones[neighbor.val] = Node(neighbor.val)
37+
38+
clones[current_node.val].neighbors.append(clones[neighbor.val])
39+
40+
return clones[node.val]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* 두 λ¬Έμžμ—΄μ˜ 졜μž₯ 곡톡 λΆ€λΆ„ λ¬Έμžμ—΄μ˜ 길이 κ΅¬ν•˜κΈ°.
3+
*
4+
* @param {string} text1 - 첫 번째 λ¬Έμžμ—΄
5+
* @param {string} text2 - 두 번째 λ¬Έμžμ—΄
6+
* @returns {number} - 졜μž₯ 곡톡 λΆ€λΆ„ λ¬Έμžμ—΄μ˜ 길이
7+
*
8+
* μ‹œκ°„ λ³΅μž‘λ„:
9+
* - O(m * n) : 두 λ¬Έμžμ—΄μ˜ 길이λ₯Ό 각각 m, n이라고 ν•  λ•Œ, DP ν…Œμ΄λΈ”μ˜ λͺ¨λ“  μš”μ†Œλ₯Ό κ³„μ‚°ν•©λ‹ˆλ‹€.
10+
*
11+
* 곡간 λ³΅μž‘λ„:
12+
* - O(m * n) : m+1 x n+1 크기의 2차원 DP ν…Œμ΄λΈ”μ„ μƒμ„±ν•˜μ—¬ μ‚¬μš©ν•©λ‹ˆλ‹€.
13+
*/
14+
function longestCommonSubsequence(text1: string, text2: string): number {
15+
const m = text1.length;
16+
const n = text2.length;
17+
18+
// DP ν…Œμ΄λΈ” 생성 (m+1 x n+1 크기)
19+
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
20+
21+
// DP 계산
22+
for (let i = 1; i <= m; i++) {
23+
for (let j = 1; j <= n; j++) {
24+
if (text1[i - 1] === text2[j - 1]) {
25+
// λ¬Έμžκ°€ μΌμΉ˜ν•˜λ©΄ 이전 값에 +1
26+
dp[i][j] = dp[i - 1][j - 1] + 1;
27+
} else {
28+
// λ¬Έμžκ°€ λ‹€λ₯΄λ©΄ μ™Όμͺ½κ³Ό μœ„μͺ½ κ°’ 쀑 큰 κ°’ 선택
29+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
30+
}
31+
}
32+
}
33+
34+
// 졜μž₯ 곡톡 λΆ€λΆ„ λ¬Έμžμ—΄μ˜ 길이
35+
return dp[m][n];
36+
}
37+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'''
2+
# 1143. Longest Common Subsequence
3+
4+
use DP table.
5+
6+
> key: tuple(current index in text1, current index in text2)
7+
> value: LCS of text1[i:] and text2[j:]
8+
'''
9+
class Solution:
10+
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
11+
dp = {}
12+
13+
def helper(i, j):
14+
if i == len(text1) or j == len(text2):
15+
return 0
16+
17+
if (i, j) in dp:
18+
return dp[(i, j)]
19+
20+
if text1[i] == text2[j]:
21+
dp[(i, j)] = 1 + helper(i + 1, j + 1)
22+
else:
23+
dp[(i, j)] = max(helper(i + 1, j), helper(i, j + 1))
24+
25+
return dp[(i, j)]
26+
27+
return helper(0, 0)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'''
2+
μ‹œκ°„ λ³΅μž‘λ„: O(m * n)
3+
곡간 λ³΅μž‘λ„: O(n)
4+
'''
5+
6+
class Solution:
7+
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
8+
m, n = len(text1), len(text2)
9+
prev = [0] * (n + 1)
10+
11+
for i in range(1, m + 1):
12+
curr = [0] * (n + 1)
13+
for j in range(1, n + 1):
14+
if text1[i - 1] == text2[j - 1]:
15+
curr[j] = prev[j - 1] + 1
16+
else:
17+
curr[j] = max(prev[j], curr[j - 1])
18+
prev = curr # ν˜„μž¬ 행을 이전 ν–‰μœΌλ‘œ μ—…λ°μ΄νŠΈ
19+
20+
return prev[n]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package leetcode_study
2+
3+
/*
4+
* 문제λ₯Ό 해결에 @Dale λ‹˜μ˜ μ„€λͺ… μ°Έκ³ . Sliding Window 기법을 톡해 λ¬Έμžμ—΄ 처리
5+
* μ‹œκ°„ λ³΅μž‘λ„: O(n)
6+
* -> μ£Όμ–΄μ§„ λ¬Έμžμ—΄μ„ μˆœνšŒν•˜λ©΄μ„œ λ¬Έμžμ—΄ 처리: O(n)
7+
* -> 문자의 λΉˆλ„ κ°±μ‹  μ—°μ‚°: O(1)
8+
*
9+
* 곡간 λ³΅μž‘λ„: O(1)
10+
* -> 문자의 개수λ₯Ό μ €μž₯ν•˜λŠ”λ° Map 자료ꡬ쑰 μ‚¬μš©. μ΅œλŒ€ 26개의 λŒ€λ¬Έμž μ €μž₯ 곡간 ν•„μš”: O(1)
11+
* */
12+
fun characterReplacement(s: String, k: Int): Int {
13+
var maxLen = 0
14+
val counter = mutableMapOf<Char, Int>()
15+
var start = 0
16+
17+
for (end in s.indices) {
18+
counter[s[end]] = counter.getOrDefault(s[end], 0) + 1 // character mapping
19+
20+
// λΆ€λΆ„ λ¬Έμžμ—΄μ˜ κΈΈμ΄μ—μ„œ κ°€μž₯ 많이 λ“€μ–΄μžˆλŠ” κΈ€μžμ˜ 수λ₯Ό λΊ€ 값이 k보닀 클 경우 μ‹œμž‘(start) 포인터λ₯Ό 이동
21+
// k 만큼 λ³€κ²½ν–ˆμ„ λ•Œ, 연속할 수 μžˆλŠ” 문자λ₯Ό λ§Œλ“€ 수 μžˆλ„λ‘ μ‘°μ •
22+
while (end - start + 1 - (counter.values.maxOrNull() ?: 0) > k) {
23+
counter[s[start]] = counter.getOrDefault(s[start], 0) - 1
24+
start++
25+
}
26+
27+
// νƒμƒ‰ν•œ λΆ€λΆ„ λ¬Έμžμ—΄ 쀑 κ°€μž₯ κΈ΄ λ¬Έμžμ—΄μ˜ 값을 μ €μž₯
28+
maxLen = maxOf(maxLen, end - start + 1)
29+
}
30+
return maxLen
31+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* sliding window
3+
* λ¬Έμžμ—΄ k번 λ³€κ²½ κ°€λŠ₯ν•œ, κ°€μž₯ κΈ΄ 반볡 λ¬Έμžμ—΄μ˜ 길이 κ΅¬ν•˜κΈ°.
4+
* @param {string} s - λ¬Έμžμ—΄
5+
* @param {number} k - 문자 λ³€κ²½ κ°€λŠ₯ 횟수
6+
* @return {number} - κ°€μž₯ κΈ΄ λΆ€λΆ„ λ¬Έμžμ—΄μ˜ 길이
7+
*
8+
* μ‹œκ°„ 볡접도: O(n)
9+
* - 문자길이 만큼의 1회 순회
10+
*
11+
* 곡간 λ³΅μž‘λ„: O(1)
12+
* - μ•ŒνŒŒλ²³ 26개의 μ œν•œλœ Map μ‚¬μš©
13+
*
14+
*/
15+
function characterReplacement(s: string, k: number): number {
16+
// 문자의 λΉˆλ„λ₯Ό μ €μž₯ν•  Map
17+
const charFreq = new Map<string, number>();
18+
19+
// window μ™Όμͺ½ 포인터
20+
let left = 0;
21+
22+
// μ΅œλŒ€ 길이, μ΅œλŒ€ λΉˆλ„ μ΄ˆκΈ°ν™”
23+
let maxLength = 0;
24+
let maxFreq = 0;
25+
26+
// 였λ₯Έμͺ½ 포인터λ₯Ό 0λΆ€ν„° μ΄λ™ν•˜λ©° window 크기 쑰절
27+
for (let right = 0; right < s.length; right++) {
28+
const char = s[right];
29+
30+
// ν˜„μž¬ 문자의 λΉˆλ„ 증가
31+
charFreq.set(char, (charFreq.get(char) || 0) + 1);
32+
33+
// μœˆλ„μš° λ‚΄μ—μ„œ κ°€μž₯ 많이 λ“±μž₯ν•œ 문자의 λΉˆλ„ κ°±μ‹ 
34+
maxFreq = Math.max(maxFreq, charFreq.get(char)!);
35+
36+
// 쑰건: (μœˆλ„μš° 크기 - maxFreq > k)일 λ•Œ, μ™Όμͺ½ 포인터 이동
37+
if (right - left + 1 - maxFreq > k) {
38+
const leftChar = s[left];
39+
charFreq.set(leftChar, charFreq.get(leftChar)! - 1); // μ™Όμͺ½ 문자 제거
40+
left++; // μœˆλ„μš° μΆ•μ†Œ
41+
}
42+
43+
// ν˜„μž¬ μœˆλ„μš° 크기λ₯Ό κΈ°μ€€μœΌλ‘œ μ΅œλŒ€ 길이 κ°±μ‹ 
44+
maxLength = Math.max(maxLength, right - left + 1);
45+
}
46+
47+
return maxLength;
48+
49+
}
50+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'''
2+
# 424. Longest Repeating Character Replacement
3+
4+
use sliding window to find the longest substring with at most k replacements.
5+
6+
## Time and Space Complexity
7+
8+
```
9+
TC: O(n)
10+
SC: O(1)
11+
```
12+
'''
13+
14+
class Solution:
15+
def characterReplacement(self, s: str, k: int) -> int:
16+
freq = [0] * 26 # A~Z, SC: O(1)
17+
left = 0
18+
max_freq = 0
19+
result = 0
20+
21+
for right in range(len(s)): # TC: O(n)
22+
curr_char_index = ord(s[right]) - 65
23+
freq[curr_char_index] += 1
24+
max_freq = max(max_freq, freq[curr_char_index])
25+
26+
if (right - left + 1) - max_freq > k:
27+
freq[ord(s[left]) - 65] -= 1
28+
left += 1
29+
30+
result = max(result, right - left + 1)
31+
32+
return result
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'''
2+
μ‹œκ°„λ³΅μž‘λ„: O(n)
3+
- λ¬Έμžμ—΄μ˜ 길이만큼 ν•œ 번만 μˆœνšŒν•©λ‹ˆλ‹€.
4+
κ³΅κ°„λ³΅μž‘λ„: O(n)
5+
- char_count λ”•μ…”λ„ˆλ¦¬λŠ” μ΅œλŒ€ μ•ŒνŒŒλ²³ 26개만 μ €μž₯ν•©λ‹ˆλ‹€.
6+
'''
7+
8+
class Solution:
9+
def characterReplacement(self, s: str, k: int) -> int:
10+
left = 0
11+
max_count = 0
12+
max_length = 0
13+
char_count = {}
14+
15+
for right in range(len(s)):
16+
char_count[s[right]] = char_count.get(s[right], 0) + 1
17+
max_count = max(max_count, char_count[s[right]])
18+
19+
# If the remaining characters exceed the allowed k changes
20+
while (right - left + 1) - max_count > k:
21+
char_count[s[left]] -= 1
22+
left += 1
23+
24+
max_length = max(max_length, right - left + 1)
25+
26+
return max_length

0 commit comments

Comments
Β (0)