Skip to content
2 changes: 1 addition & 1 deletion climbing-stairs/hongseoupyun.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def climbStairs(self, n: int) -> int:

prev1 = 1
prev2 = 2
for i in range(n,n+1):
for i in range(3,n+1):
current = prev2 + prev1
prev1 = prev2
prev2 = current
Expand Down
95 changes: 95 additions & 0 deletions combination-sum/hongseoupyun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# // Input: candidates = [2,3,5], target = 8
# // Output: [[2,2,2,2],[2,3,3],[3,5]]

# // DFS/backtracking should be used - Find possilbe combinations till the end and backtrack to previous step to find other combinations
# // For example, if we have candidates [2,3,5] and target 8
# // Start with empty combination [], target 8
# // Add 2 -> [2], remaining 6
# // Add 2 -> [2,2], remaining 4
# // Add 2 -> [2,2,2], remaining 2
# // Add 2 -> [2,2,2,2], remaining 0 (found a valid combination)
# // Backtrack to [2,2,2,3], remaining -1 (remaining is negative, backtrack)
# // Backtrack to [2,2,2,5], remaining -3 (remaining is negative, backtrack)
# // Backtrack to [2,2,3], remaining 1 (dead end, backtrack)
# // Backtrack to [2,2,5], remaining -3 (remaining is negative, backtrack)
# // Backtrack to [2,3], remaining 3
# // Add 3 -> [2,3,3], remaining 0 (found a valid combination)
# // so on ..

# // When you dfs, always start from current index and allow reusing the same element or next index only to avoid duplicates / to get unique combination
# // Hence, if we starts from 2, next dfs calls can start from index of 2 or index of 3, but not index of 5 directly
# // Moreover, if we start from 3, next dfs calls can start from index of 3 or index of 5, but not index of 2 directly

# backtrack(8, [], 0)
# │
# ├─ i = 0 pick 2 → backtrack(6, [2], 0)
# │ │
# │ ├─ i = 0 pick 2 → backtrack(4, [2,2], 0)
# │ │ │
# │ │ ├─ i = 0 pick 2 → backtrack(2, [2,2,2], 0)
# │ │ │ │
# │ │ │ ├─ i = 0 pick 2 → backtrack(0, [2,2,2,2], 0) 🟢 success → return
# │ │ │ │
# │ │ │ ├─ i = 1 pick 3 → backtrack(-1, [2,2,2,3], 1) 🔴 → return
# │ │ │ │
# │ │ │ └─ i = 2 pick 5 → backtrack(-3, [2,2,2,5], 2) 🔴 → return
# │ │ │
# │ │ └─ loop end → pop → combination = [2,2]
# │ │
# │ ├─ i = 1 pick 3 → backtrack(1, [2,2,3], 1)
# │ │ │
# │ │ ├─ i = 1 pick 3 → backtrack(0, [2,2,3,3], 1) 🟢 success → return
# │ │ │
# │ │ └─ i = 2 pick 5 → backtrack(-4, [2,2,3,5], 2) 🔴 → return
# │ │
# │ ├─ loop end → pop → [2,2]
# │ │
# │ └─ i = 2 pick 5 → backtrack(-1, [2,2,5], 2) 🔴 → return
# │
# ├─ loop end → pop → [2]
# │
# ├─ i = 1 pick 3 → backtrack(3, [2,3], 1)
# │ │
# │ ├─ i = 1 pick 3 → backtrack(0, [2,3,3], 1) 🟢 success → return
# │ │
# │ └─ i = 2 pick 5 → backtrack(-2, [2,3,5], 2) 🔴 → return
# │
# ├─ loop end → pop → [2]
# │
# ├─ i = 2 pick 5 → backtrack(1, [2,5], 2)
# │ │
# │ └─ i = 2 pick 5 → backtrack(-4, [2,5,5], 2) 🔴 → return
# │
# └─ loop end → pop → []




from typing import List

class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
result: List[List[int]] = []

def backtrack(remaining: int, combination: List[int], start_index: int):
# base cases
if remaining == 0:
# append a COPY of combination
result.append(combination[:])
return
if remaining < 0:
return

# try candidates from start_index onward
for i in range(start_index, len(candidates)):
current_number = candidates[i]
# choose
combination.append(current_number)
# explore (i, not i+1, because we can reuse same element)
backtrack(remaining - current_number, combination, i)
# undo (backtrack)
combination.pop()

# initial call
backtrack(target, [], 0)
return result
100 changes: 100 additions & 0 deletions combination-sum/hongseoupyun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Input: candidates = [2,3,5], target = 8
// Output: [[2,2,2,2],[2,3,3],[3,5]]

// DFS/backtracking should be used - Find possilbe combinations till the end and backtrack to previous step to find other combinations
// For example, if we have candidates [2,3,5] and target 8
// Start with empty combination [], target 8
// Add 2 -> [2], remaining 6
// Add 2 -> [2,2], remaining 4
// Add 2 -> [2,2,2], remaining 2
// Add 2 -> [2,2,2,2], remaining 0 (found a valid combination)
// Backtrack to [2,2,2,3], remaining -1 (remaining is negative, backtrack)
// Backtrack to [2,2,2,5], remaining -3 (remaining is negative, backtrack)
// Backtrack to [2,2,3], remaining 1 (dead end, backtrack)
// Backtrack to [2,2,5], remaining -3 (remaining is negative, backtrack)
// Backtrack to [2,3], remaining 3
// Add 3 -> [2,3,3], remaining 0 (found a valid combination)
// so on ..

// When you dfs, always start from current index and allow reusing the same element or next index only to avoid duplicates / to get unique combination
// Hence, if we starts from 2, next dfs calls can start from index of 2 or index of 3, but not index of 5 directly
// Moreover, if we start from 3, next dfs calls can start from index of 3 or index of 5, but not index of 2 directly

/*
backtrack(8, [], 0)
├─ i = 0 pick 2 → backtrack(6, [2], 0)
│ │
│ ├─ i = 0 pick 2 → backtrack(4, [2,2], 0)
│ │ │
│ │ ├─ i = 0 pick 2 → backtrack(2, [2,2,2], 0)
│ │ │ │
│ │ │ ├─ i = 0 pick 2 → backtrack(0, [2,2,2,2], 0) 🟢 success → return
│ │ │ │
│ │ │ ├─ i = 1 pick 3 → backtrack(-1, [2,2,2,3], 1) 🔴 → return
│ │ │ │
│ │ │ └─ i = 2 pick 5 → backtrack(-3, [2,2,2,5], 2) 🔴 → return
│ │ │
│ │ └─ loop end → pop → combination = [2,2]
│ │
│ ├─ i = 1 pick 3 → backtrack(1, [2,2,3], 1)
│ │ │
│ │ ├─ i = 1 pick 3 → backtrack(0, [2,2,3,3], 1) 🟢 success → return
│ │ │
│ │ └─ i = 2 pick 5 → backtrack(-4, [2,2,3,5], 2) 🔴 → return
│ │
│ ├─ loop end → pop → [2,2]
│ │
│ └─ i = 2 pick 5 → backtrack(-1, [2,2,5], 2) 🔴 → return
├─ loop end → pop → [2]
├─ i = 1 pick 3 → backtrack(3, [2,3], 1)
│ │
│ ├─ i = 1 pick 3 → backtrack(0, [2,3,3], 1) 🟢 success → return
│ │
│ └─ i = 2 pick 5 → backtrack(-2, [2,3,5], 2) 🔴 → return
├─ loop end → pop → [2]
├─ i = 2 pick 5 → backtrack(1, [2,5], 2)
│ │
│ └─ i = 2 pick 5 → backtrack(-4, [2,5,5], 2) 🔴 → return
└─ loop end → pop → []
*/


function combinationSum(candidates: number[], target: number): number[][] {
const result: number[][] = []

function backtrack(remaining: number, combination: number[], startindex: number) {
// Remaining is target - sum of combination
// Base Case1: If remaining is 0, we found a valid combination -> add that combinations to result
if (remaining === 0) {
result.push([...combination]);
return;
}

// Base Case2: If remaining is negative, no valid combination -> backtrack
if (remaining < 0) {
return;
}

// Explore further by trying each candidate starting from startindex
// candidates = [2,3,5], target = 8
for (let i = startindex; i < candidates.length; i++) {
// Choose the current number
const currentNumber = candidates[i];
// Add the current number to the combination
combination.push(currentNumber);

// Explore further with updated remaining and current combination
backtrack(remaining - currentNumber, combination, i);
// Backtrack: remove the last added candidate to try next candidate
combination.pop();
}
}
backtrack(target, [], 0);
return result;
};
44 changes: 44 additions & 0 deletions valid-palindrome/hongseoupyun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

import re

class Solution:
# Palindrome is a word, phrase, number, or other sequences of characters that reads the same forward and backward (ignoring non-alphanumeric characters - letters and number).
# the solution should first filter out non-alphanumeric characters and convert the string to lowercase.
# Then, it should check if the cleaned string is equal to its reverse.

# Time complexity = O(n), as it checks, replaces, converts to lowercase, and reverses all characters in the string
# Space complexity = O(n), as it creates a new string with a maximum length equal to the original string
def isPalindrome(self, s: str) -> bool:
filtered_string = re.sub(r'[^a-zA-Z0-9]','',s).lower()
reversed_string = filtered_string[::-1]
return reversed_string == filtered_string


# Using Two-pointer; It searches from given string directly so the Space complexity is O(n) - better method
# By using two index, searchs and compares from start and end of the string like this -><-
# Do this while start index is less than end index
# Skip non-alphanumeric characters
# If both pointer are in characters, then convert to lowercase and compare
# If not equal, return false
# If all characters are equal, return true

# Time complexity = O(n), as it checks and converts to lowercase all characters in the string
# Space complexity = O(1), as it uses only a constant amount of extra space
def isPalindromeTwoPointer(self, s: str) -> bool:
left = 0
right = len(s) - 1

while left < right:
if not s[left].isalnum():
left+=1
Copy link
Contributor

Choose a reason for hiding this comment

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

이렇게 two pointer를 사용한다면 공간 복잡도를 O(1)으로 최적화 할 수 있네요! 덕분에 좋은 풀이법 알아갑니다 ><

continue
if not s[right].isalnum():
right-=1
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

이렇게 if-continue 문 대신 내부에서 다시 while 문으로 left < right 범위 내에서 left / right 포인터를 이동시킨다면 더 간결한 코드가 될 것 같아요!

if s[left].lower() != s[right].lower():
return False

left+=1
right-=1

return True
56 changes: 56 additions & 0 deletions valid-palindrome/hongseoupyun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// # Palindrome is a word, phrase, number, or other sequences of characters that reads the same forward and backward (ignoring non-alphanumeric characters - letters and number).
// # the solution should first filter out non-alphanumeric characters and convert the string to lowercase.
// # Then, it should check if the cleaned string is equal to its reverse.


// Time complexity = O(n), as it checks, replaces, converts to lowercase, and reverses all characters in the string
// Space complexity = O(n), as it creates a new string with a maximum length equal to the original string
function isPalindrome(s: string): boolean {
let filteredString: string = s.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
let reversed: string = filteredString.split("").reverse().join("")

if (filteredString === reversed) {
return true
} else {
return false
}
};

//Palindrom example: "A man, a plan, a canal: Panama"

// Using Two-pointer; It searches from given string directly so the Space complexity is O(n) - better method
// By using two index, searchs and compares from start and end of the string like this-><-
// Do this while start index is less than end index
// Skip non-alphanumeric characters
// If both pointer are in characters, then convert to lowercase and compare
// If not equal, return false
// If all characters are equal, return true

// Time complexity = O(n), as it checks and converts to lowercase all characters in the string
// Space complexity = O(1), as it uses only a constant amount of extra space
function isPalindrome2(s: string): boolean {

let left: number = 0;
let right: number = s.length - 1;

let isAlphaNum = (str: string): boolean => {
return /^[a-zA-Z0-9]$/.test(str)
}

while (left < right) {
if (!isAlphaNum(s[left])) {
left++
continue
}
if (!isAlphaNum(s[right])) {
right--
continue
}
if (s[left].toLowerCase() !== s[right].toLowerCase()) {
return false
}
left++
right--
}
return true
};