Skip to content

[clara-shin] Week 08 Solutions #1494

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

Merged
merged 6 commits into from
May 25, 2025
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
37 changes: 37 additions & 0 deletions clone-graph/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* BFS를 사용한 그래프 복제
* @param {_Node} node
* @return {_Node}
*/
var cloneGraph = function (node) {
if (!node) return null;

// 원본 노드와 복제된 노드를 매핑하는 해시맵
const cloned = new Map();

// BFS를 위한 큐
const queue = [node];

// 시작 노드 복제
cloned.set(node, new _Node(node.val));

while (queue.length > 0) {
const currentNode = queue.shift();

// 현재 노드의 모든 이웃들을 처리
for (let neighbor of currentNode.neighbors) {
// 이웃이 아직 복제되지 않았다면
if (!cloned.has(neighbor)) {
// 새로운 노드 생성하고 맵에 저장
cloned.set(neighbor, new _Node(neighbor.val));
// 큐에 추가하여 나중에 처리
queue.push(neighbor);
}

// 복제된 현재 노드의 이웃 리스트에 복제된 이웃 추가
cloned.get(currentNode).neighbors.push(cloned.get(neighbor));
}
}

return cloned.get(node);
};
40 changes: 40 additions & 0 deletions longest-common-subsequence/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 두 문자열에서 공통으로 나타나는 부분 수열 중 가장 긴 것의 길이를 찾는 문제
*
* 다이나믹 프로그래밍(DP): 작은 문제의 결과를 저장해서 큰 문제를 해결하는 방법
* 시간 복잡도: O(m × n)
* 공간복잡도: O(min(m, n))
*/
/**
* @param {string} text1
* @param {string} text2
* @return {number}
*/
var longestCommonSubsequence = function (text1, text2) {
// 더 짧은 문자열을 text2로 만들어 공간 절약
if (text1.length < text2.length) {
[text1, text2] = [text2, text1];
}

const m = text1.length;
const n = text2.length;

// 1차원 배열 2개만 사용 (이전 행, 현재 행)
let prev = Array(n + 1).fill(0);
let curr = Array(n + 1).fill(0);

for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (text1[i - 1] === text2[j - 1]) {
curr[j] = prev[j - 1] + 1;
} else {
curr[j] = Math.max(prev[j], curr[j - 1]);
}
}
// 다음 반복을 위해 배열 교체
[prev, curr] = [curr, prev];
curr.fill(0);
}

return prev[n];
};
51 changes: 51 additions & 0 deletions longest-repeating-character-replacement/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 최대 k번 문자 변경 후, 가장 긴 동일문자 부분문자열 찾기
* 문자열 s와 정수 k, 최대 k번까지 문자를 다른 영문대문자로 바꿀 수 있음
* 동일한 문자로 구성된 가장 긴 부분문자열 길이를 리턴해라
*
* 슬라이딩 윈도우 사용(문자열을 한번만 순회)
* 윈도우 내에서, 가장 많이 등장하는 문자를 제외한 나머지 문자들이, k개 이하일 때만, 유효한 부분 문자열이다
* 조건을 만족하지 않으면 윈도우의 왼쪽 포인터를 오른쪽으로 이동하여 윈도우 크기를 줄인다
*
* 시간복잡도 O(n)
* 공간복잡도 O(1) - 문자 빈도수 저장배열 크기는 항상 26 고정
*/

/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var characterReplacement = function (s, k) {
// 문자 빈도수를 저장할 배열 (A-Z: 65-90) 0으로 초기화
const charCount = new Array(26).fill(0);

let left = 0; // 윈도우 왼쪽 끝
let maxCount = 0; // 현재 윈도우에서 가장 많이 등장하는 문자의 빈도수
let maxLength = 0; // 결과: 가장 긴 부분 문자열 길이

// 윈도우의 오른쪽 끝 이동
for (let right = 0; right < s.length; right++) {
// 현재 문자의 빈도수 증가 (문자 코드를 인덱스로 변환)
const charIndex = s.charCodeAt(right) - 65;
charCount[charIndex]++;

// 현재 윈도우에서 가장 많이 등장하는 문자의 빈도수 업데이트
maxCount = Math.max(maxCount, charCount[charIndex]);

// 현재 윈도우에서 변경해야 할 문자 수가 k를 초과하면 윈도우의 왼쪽을 이동
// 현재 윈도우에서 변경해야 할 문자 수: 윈도우 크기 - 가장 많이 등장하는 문자의 빈도수
// 윈도우 크기: right - left + 1
if (right - left + 1 - maxCount > k) {
// 왼쪽 문자의 빈도수 감소
const leftCharIndex = s.charCodeAt(left) - 65;
charCount[leftCharIndex]--;
left++;
}

// 현재 윈도우 크기로 최대 길이 업데이트
maxLength = Math.max(maxLength, right - left + 1);
}

return maxLength;
};
46 changes: 46 additions & 0 deletions palindromic-substrings/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 주어진 문자열에서 팰린드롬(앞뒤로 읽어도 같은 문자열)인 부분 문자열의 개수를 구하는 문제
*
* 투 포인터(Two Pointers) vs. 중심확장(Center Expansion)
* 투포인터: 보통 정렬된 배열에서 두 요소의 합을 찾는 등에 사용
*
* 중심확장이 더 적합한 이유:
* 팰린드롬의 특성(중심 대칭)을 직접적으로 활용
* 불필요한 비교 최소화
* 구현이 간단하고 이해하기 쉬움
*
* 이번 문제는: Center Expansion 방법으로 접근
* 모든 부분 문자열을 확인해야 함
* 각 위치에서 팰린드롬 가능성을 체크해야 함
*
* 시간 복잡도: O(n²) - 각 중심에서 최대 n번 확장
* 공간 복잡도: O(1) - 추가 공간 거의 불필요
*/
/**
* @param {string} s
* @return {number}
*/
var countSubstrings = function (s) {
if (!s || s.length === 0) return 0; // 빈 배열이면 빠른 리턴

let count = 0;
const n = s.length;

// 중심 확장 함수
function expandAroundCenter(left, right) {
while (left >= 0 && right < n && s[left] === s[right]) {
count++;
left--;
right++;
}
}

for (let i = 0; i < n; i++) {
// 홀수 길이 팰린드롬
expandAroundCenter(i, i);
// 짝수 길이 팰린드롬
expandAroundCenter(i, i + 1);
}

return count;
};
31 changes: 31 additions & 0 deletions reverse-bits/clara-shin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 비트 반전 문제(32비트 무부호 정수, 32 bits unsigned integer)
* 비트 반전: 32비트 전체를 뒤집는 것(왼쪽과 오른쪽을 바꾸는 것)
*
* 접근 방법:
* 32비트 정수의 각 비트를 오른쪽에서 왼쪽으로 순회하며 확인
* 각 비트를 왼쪽에서 오른쪽으로 반대로 위치시킴
* 각 위치에 따라 결과값에 더함
*
* 시간복잡도: O(1)
* 공간복잡도: O(1)
*/
/**
* @param {number} n - a positive integer
* @return {number} - a positive integer
*/
var reverseBits = function (n) {
let result = 0;

for (let i = 0; i < 32; i++) {
// 결과에 현재 비트를 추가 (비트 OR 연산)
// 결과를 왼쪽으로 시프트한 후, n의 최하위 비트가 1이면 결과에 1을 더함
result = (result << 1) | (n & 1);

// n을 오른쪽으로 시프트
n >>>= 1;
}

// 부호 없는 정수로 변환
return result >>> 0;
};