diff --git a/clone-graph/Jeehay28.ts b/clone-graph/Jeehay28.ts new file mode 100644 index 000000000..166916026 --- /dev/null +++ b/clone-graph/Jeehay28.ts @@ -0,0 +1,67 @@ +class _Node { + val: number; + neighbors: _Node[]; + + constructor(val?: number, neighbors?: _Node[]) { + this.val = val === undefined ? 0 : val; + this.neighbors = neighbors === undefined ? [] : neighbors; + } +} + +// TC: O(V + E), where V is the number of vertices and E is the number of edges +// SC: O(V + E) +function cloneGraph(node: _Node | null): _Node | null { + // 1: [2, 4] + // 2: [1, 3] + // 3: [2, 4] + // 4: [1, 3] + + const clones = new Map<_Node, _Node>(); + // original Node: cloned Node + + if (!node) return null; + + const dfs = (node: _Node) => { + if (clones.has(node)) { + return clones.get(node); + } + + const clone = new _Node(node.val); + clones.set(node, clone); + + for (const nei of node.neighbors) { + clone.neighbors.push(dfs(nei)!); + } + + return clone; + }; + + return dfs(node)!; +} + + +// TC: O(V + E) +// SC: O(V + E) +// function cloneGraph(node: _Node | null): _Node | null { +// if (!node) return null; + +// const clone: _Node = new _Node(node.val); +// const clones = new Map<_Node, _Node>(); +// clones.set(node, clone); +// const queue: _Node[] = [node]; // BFS -> use queue + +// while (queue.length > 0) { +// const node = queue.shift()!; +// for (const nei of node.neighbors) { +// if (!clones.has(nei)) { +// clones.set(nei, new _Node(nei.val)); +// queue.push(nei); +// } +// clones.get(node)!.neighbors.push(clones.get(nei)!); +// } +// } + +// return clone; +// } + + diff --git a/longest-common-subsequence/Jeehay28.ts b/longest-common-subsequence/Jeehay28.ts new file mode 100644 index 000000000..f9894986d --- /dev/null +++ b/longest-common-subsequence/Jeehay28.ts @@ -0,0 +1,49 @@ +// TC: O(m * n) +// SC: O(m * n) +function longestCommonSubsequence(text1: string, text2: string): number { + const memo = new Map(); + + const dfs = (i: number, j: number) => { + const key = `${i}-${j}`; + + if (memo.has(key)) return memo.get(key); + + if (i === text1.length || j === text2.length) { + // "" + memo.set(key, 0); + } else if (text1[i] === text2[j]) { + memo.set(key, 1 + dfs(i + 1, j + 1)!); + } else { + memo.set(key, Math.max(dfs(i + 1, j)!, dfs(i, j + 1)!)); + } + + return memo.get(key); + }; + + return dfs(0, 0)!; +} + + +// TC: O(m * n) +// SC: O(m * n) +// function longestCommonSubsequence(text1: string, text2: string): number { +// const m = text1.length + 1; +// const n = text2.length + 1; + +// const dp: number[][] = Array.from({ length: m }, () => Array(n).fill(0)); + +// for (let i = 1; i < m; i++) { +// for (let j = 1; j < n; j++) { +// // Note: text1[i - 1] and text2[j - 1] because dp uses 1-based indexing, +// // while the strings use 0-based indexing +// if (text1[i - 1] === text2[j - 1]) { +// dp[i][j] = 1 + dp[i - 1][j - 1]; +// } else { +// dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); +// } +// } +// } + +// return dp[m - 1][n - 1]; +// } + diff --git a/longest-repeating-character-replacement/Jeehay28.ts b/longest-repeating-character-replacement/Jeehay28.ts new file mode 100644 index 000000000..13c81ac6f --- /dev/null +++ b/longest-repeating-character-replacement/Jeehay28.ts @@ -0,0 +1,27 @@ +// TC: O(n) +// SC: O(1) +function characterReplacement(s: string, k: number): number { + const freqMap = new Map(); + let left = 0; + let maxFreqCnt = 0; + let result = 0; + + for (let right = 0; right < s.length; right++) { + const ch = s[right]; + freqMap.set(ch, (freqMap.get(ch)! | 0) + 1); + maxFreqCnt = Math.max(maxFreqCnt, freqMap.get(ch)!); + + while (right - left + 1 - maxFreqCnt > k) { + // Shrink the sliding window by moving the left pointer to the right. + // As we move left, decrease the frequency count of the character being excluded from the window. + const ch = s[left]; + freqMap.set(ch, freqMap.get(ch)! - 1); + left++; + } + + result = Math.max(result, right - left + 1); + } + + return result; +} + diff --git a/palindromic-substrings/Jeehay28.ts b/palindromic-substrings/Jeehay28.ts new file mode 100644 index 000000000..935e6139f --- /dev/null +++ b/palindromic-substrings/Jeehay28.ts @@ -0,0 +1,97 @@ +// TC: O(n^2) +// SC: O(1) +function countSubstrings(s: string): number { + // Treat each index as the center of potential palindromic substrings + let cnt = 0; + + for (let i = 0; i < s.length; i++) { + let start = i; + let end = i; + + // Check for odd-length palindromes centered at i + while (start >= 0 && end < s.length && s[start] === s[end]) { + cnt++; + start--; // <---- + end++; // -----> + } + + // Check for even-length palindromes centered between i and i + 1 + start = i; + end = i + 1; + + while (start >= 0 && end < s.length && s[start] === s[end]) { + cnt++; + start--; + end++; + } + } + + return cnt; +} + +// TC: O(n^2) +// SC: O(n^2) +/* +function countSubstrings(s: string): number { + // dp[(start, end)] = s[start] === s[end] && dp[(start + 1, end - 1)] + + // A new starting character + an existing palindromic substring + a new ending character + // start dp[(start + 1, end - 1)] end + + const dp = new Map(); + + for (let end = 0; end < s.length; end++) { + // start ranges from end down to 0 + for (let start = end; start >= 0; start--) { + const key = `${start}-${end}`; + if (start === end) { + // "a" + dp.set(key, true); + } else if (start + 1 === end) { + // "aa" + dp.set(key, s[start] === s[end]); + } else { + const prevKey = `${start + 1}-${end - 1}`; + dp.set(key, s[start] === s[end] && dp.get(prevKey) === true); + } + } + } + + let cnt = 0; + + for (const [key, value] of dp.entries()) { + if (value) { + cnt++; + } + } + + return cnt; +} +*/ + +// TC: O(n^3) +// SC: O(1) +// function countSubstrings(s: string): number { +// const isPalindromic = (left: number, right: number) => { +// while (left < right) { +// if (s[left] !== s[right]) return false; + +// left++; +// right--; +// } + +// return true; +// }; + +// let cnt = 0; + +// for (let i = 0; i < s.length; i++) { +// for (let j = i; j < s.length; j++) { +// if (isPalindromic(i, j)) { +// cnt++; +// } +// } +// } + +// return cnt; +// } diff --git a/reverse-bits/Jeehay28.ts b/reverse-bits/Jeehay28.ts new file mode 100644 index 000000000..970637212 --- /dev/null +++ b/reverse-bits/Jeehay28.ts @@ -0,0 +1,70 @@ +// TC: O(1) +// SC: O(1) +function reverseBits(n: number): number { + let stack: number[] = []; + + while (stack.length < 32) { + stack.push(n % 2); + n = Math.floor(n / 2); + } + + let output: number = 0; + let scale: number = 1; + + while (stack.length > 0) { + output += stack.pop()! * scale; + scale *= 2; + } + + return output; +} + +// TC: O(1) +// SC: O(1) +/* +function reverseBits(n: number): number { + let result = 0; + + for (let i = 0; i < 32; i++) { + let bit = n & 1; + result = (result << 1) | bit; + n = n >>> 1; + } + + return result >>> 0; +} +*/ + +/* +n & 1 +- get last bit +- equivalent to n % 2 for non-negative integer + +n << 1 +- left shift (multiply by 2) + +n >>> 1 +- Unsigned right shift (divide by 2) +- ignores sign + +n >>> 0 +- no shift, just cast to unsigned + +const signed = -1; // Binary: 11111111111111111111111111111111 +const unsigned = signed >>> 0; // 4294967295 + +JavaScript/TypeScript only has one number type — a 64-bit float. itwise operations use 32-bit signed integers. + To return a 32-bit unsigned integer, use >>> 0 on the result. + + 🔧 Bitwise Operations in JavaScript — Summary + 1. Bitwise operations in JavaScript (&, |, ^, ~, <<, >>, >>>) are always performed on 32-bit signed integers. + + 2. Internally: + - JavaScript converts your number into a 32-bit signed integer + - Performs the bitwise operation + - Then converts it back into a regular 64-bit number (JavaScript's only number type) + + 3. If you need the result as a 32-bit unsigned integer (like for LeetCode problems dealing with uint32): + - Use >>> 0 at the end of your result + - This forces the result to be treated as an unsigned 32-bit integer +*/