diff --git a/climbing-stairs/hongseoupyun.py b/climbing-stairs/hongseoupyun.py index 15641fc9d9..75e5ed1427 100644 --- a/climbing-stairs/hongseoupyun.py +++ b/climbing-stairs/hongseoupyun.py @@ -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 diff --git a/combination-sum/hongseoupyun.py b/combination-sum/hongseoupyun.py new file mode 100644 index 0000000000..2a712a9bf1 --- /dev/null +++ b/combination-sum/hongseoupyun.py @@ -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 diff --git a/combination-sum/hongseoupyun.ts b/combination-sum/hongseoupyun.ts new file mode 100644 index 0000000000..bac7c5179c --- /dev/null +++ b/combination-sum/hongseoupyun.ts @@ -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; +}; diff --git a/valid-palindrome/hongseoupyun.py b/valid-palindrome/hongseoupyun.py new file mode 100644 index 0000000000..9527fdb8c7 --- /dev/null +++ b/valid-palindrome/hongseoupyun.py @@ -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 + continue + if not s[right].isalnum(): + right-=1 + continue + if s[left].lower() != s[right].lower(): + return False + + left+=1 + right-=1 + + return True diff --git a/valid-palindrome/hongseoupyun.ts b/valid-palindrome/hongseoupyun.ts new file mode 100644 index 0000000000..1d71d713f9 --- /dev/null +++ b/valid-palindrome/hongseoupyun.ts @@ -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 +};