Skip to content

[yoouyeon] WEEK 03 solutions #1290

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 4 commits into from
Apr 20, 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
25 changes: 25 additions & 0 deletions combination-sum/yoouyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// [39] Combination Sum

function combinationSum(candidates: number[], target: number): number[][] {
const n = candidates.length;
const output: number[][] = [];

function dfs(arr: number[], startIdx: number, sum: number) {
if (sum === target) {
output.push([...arr]);
return;
}
if (sum > target) return;

// 현재 숫자부터 다시 시작 (중복 선택이 가능하기 때문)
for (let idx = startIdx; idx < n; idx++) {
arr.push(candidates[idx]);
dfs(arr, idx, sum + candidates[idx]);
arr.pop();
}
}

dfs([], 0, 0);

return output;
}
34 changes: 34 additions & 0 deletions decode-ways/yoouyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// [91] Decode Ways

/**
* [Time Complexity]
* O(n)
*
* [Space Complexity]
* O(n)
*/
function numDecodings(s: string): number {
const n = s.length;
const memo = new Array<number>(n + 1).fill(0);

// 첫번째 숫자가 0인 경우 디코딩이 불가능하므로 미리 0 반환
if (s[0] === "0") return 0;

memo[0] = 1; // 빈 문자열
memo[1] = 1; // 0이 아닌 첫번째 숫자 디코딩

for (let idx = 2; idx <= n; idx++) {
const oneDigit = Number(s.slice(idx - 1, idx)); // 현재 숫자
const twoDigits = Number(s.slice(idx - 2, idx)); // 현재 숫자 앞의 수와 합침

// 변환할 수 있는 경우의 수를 더해준다.
if (oneDigit >= 1 && oneDigit <= 9) {
memo[idx] += memo[idx - 1];
}
if (twoDigits >= 10 && twoDigits <= 26) {
memo[idx] += memo[idx - 2];
}
}

return memo[n];
}
19 changes: 19 additions & 0 deletions number-of-1-bits/yoouyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// [191] Number of 1 Bits

/**
* [Idea]
* 숫자를 2진수로 변환해서 (toString) 1인 비트를 직접 세줬다.
*
* [Time Complexity]
* toString 내부에서 진법 변환을 수행하는 과정에서 나눗셈을 반복적으로 수행하기 때문에 시간복잡도는 O(log n)
*
* [Space Complexity]
* 변환된 수의 자릿수만큼의 공간이 필요하므로 공간복잡도는 O(log n)
*/
function hammingWeight(n: number): number {
const binary = n.toString(2);
return [...binary].reduce(
(count, bit) => (bit === "1" ? count + 1 : count),
0
);
}
62 changes: 62 additions & 0 deletions valid-palindrome/yoouyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// [125] Valid Palindrome

/**
* Solution 1. 문자열 직접 만들어서 비교하기
*
* [Idea]
* 문제에 주어진 조건을 그대로 구현해서 풀었다.
* 소문자로 변환한 뒤에 Alphanumeric character만 남긴 cleaned 문자열과
* cleaned 문자열을 뒤집은 reversed 문자열을 만들어서
* 둘이 비교해서 같으면 팰린드롬, 아니면 팰린드롬이 아니라고 판단했다.
*
* [Time Complexity]
* 문자열을 조건에 맞게 가공하는 과정에서 s의 길이만큼 상수 번 순회하기 때문에 시간 복잡도는 O(n)
*
* [Space Complexity]
* reversed 문자열을 따로 만들어 저장하므로 공간 복잡도는 O(n)
*
*/
function isPalindrome1(s: string): boolean {
// replace: 소문자가 아니고 숫자가 아닌 것을 모두 ''으로 치환(제거)한다.
const cleaned = s.toLowerCase().replace(/[^a-z0-9]/g, "");
// string에는 reverse 내장함수가 없으므로, string -> array -> reverse -> string (join) 과정으로 뒤집는다.
const reversed = cleaned.split("").reverse().join("");

return cleaned === reversed;
}

/**
* Solution 2. 포인터를 활용하기
* [Idea]
* 문자열을 가공하는 데 필요한 추가적인 순회 시간을 줄이고 reversed를 따로 저장하지 않아도 되는 방법
* s의 왼쪽, 오른쪽을 순서대로 비교하면서 아래와 같은 과정을 거친다.
* 1. 알파벳(소문자로 변환하지 않았으므로 대소문자 모두) 또는 숫자가 아닌 경우를 제외한다 (포인터 이동)
* 2. 현재 포인터가 가리키는 두 알파벳이 소문자로 변환했을 때 같지 않으면 팰린드롬이 아니다.
* 3. 알파벳이 같다면 다음 문자를 비교하기 위해 포인터를 옮긴다.
*
* [Time Complexity]
* Solution 1과 동일하게 O(n)이지만 s를 1번만 순회한다
*
* [Space Complexity]
* left, right 포인터를 저장할 상수 크기의 메모리가 필요하므로 공간 복잡도는 O(1)이다.
*/
function isPalindrome2(s: string): boolean {
let left = 0,
right = s.length - 1;

while (left < right) {
while (left < right && s[left].match(/[^A-Za-z0-9]/)) {
left++;
}
while (left < right && s[right].match(/[^A-Za-z0-9]/)) {
right--;
}
if (s[left].toLowerCase() !== s[right].toLowerCase()) {
return false;
}
left++;
right--;
}

return true;
}