diff --git a/README.md b/README.md index d3ae25d..049db06 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 2,250+ LeetCode solutions in JavaScript +# 2,300+ LeetCode solutions in JavaScript [https://leetcodejavascript.com](https://leetcodejavascript.com) @@ -517,8 +517,11 @@ 528|[Random Pick with Weight](./solutions/0528-random-pick-with-weight.js)|Medium| 529|[Minesweeper](./solutions/0529-minesweeper.js)|Medium| 530|[Minimum Absolute Difference in BST](./solutions/0530-minimum-absolute-difference-in-bst.js)|Easy| +531|[Lonely Pixel I](./solutions/0531-lonely-pixel-i.js)|Medium| 532|[K-diff Pairs in an Array](./solutions/0532-k-diff-pairs-in-an-array.js)|Medium| +533|[Lonely Pixel II](./solutions/0533-lonely-pixel-ii.js)|Medium| 535|[Encode and Decode TinyURL](./solutions/0535-encode-and-decode-tinyurl.js)|Medium| +536|[Construct Binary Tree from String](./solutions/0536-construct-binary-tree-from-string.js)|Medium| 537|[Complex Number Multiplication](./solutions/0537-complex-number-multiplication.js)|Medium| 538|[Convert BST to Greater Tree](./solutions/0538-convert-bst-to-greater-tree.js)|Medium| 539|[Minimum Time Difference](./solutions/0539-minimum-time-difference.js)|Medium| @@ -526,29 +529,39 @@ 541|[Reverse String II](./solutions/0541-reverse-string-ii.js)|Easy| 542|[01 Matrix](./solutions/0542-01-matrix.js)|Medium| 543|[Diameter of Binary Tree](./solutions/0543-diameter-of-binary-tree.js)|Easy| +544|[Output Contest Matches](./solutions/0544-output-contest-matches.js)|Medium| +545|[Boundary of Binary Tree](./solutions/0545-boundary-of-binary-tree.js)|Medium| 546|[Remove Boxes](./solutions/0546-remove-boxes.js)|Hard| 547|[Number of Provinces](./solutions/0547-number-of-provinces.js)|Medium| +548|[Split Array with Equal Sum](./solutions/0548-split-array-with-equal-sum.js)|Hard| +549|[Binary Tree Longest Consecutive Sequence II](./solutions/0549-binary-tree-longest-consecutive-sequence-ii.js)|Medium| 551|[Student Attendance Record I](./solutions/0551-student-attendance-record-i.js)|Easy| 552|[Student Attendance Record II](./solutions/0552-student-attendance-record-ii.js)|Hard| 553|[Optimal Division](./solutions/0553-optimal-division.js)|Medium| 554|[Brick Wall](./solutions/0554-brick-wall.js)|Medium| +555|[Split Concatenated Strings](./solutions/0555-split-concatenated-strings.js)|Medium| 556|[Next Greater Element III](./solutions/0556-next-greater-element-iii.js)|Medium| 557|[Reverse Words in a String III](./solutions/0557-reverse-words-in-a-string-iii.js)|Easy| 558|[Logical OR of Two Binary Grids Represented as Quad-Trees](./solutions/0558-logical-or-of-two-binary-grids-represented-as-quad-trees.js)|Medium| 559|[Maximum Depth of N-ary Tree](./solutions/0559-maximum-depth-of-n-ary-tree.js)|Easy| 560|[Subarray Sum Equals K](./solutions/0560-subarray-sum-equals-k.js)|Medium| 561|[Array Partition](./solutions/0561-array-partition.js)|Easy| +562|[Longest Line of Consecutive One in Matrix](./solutions/0562-longest-line-of-consecutive-one-in-matrix.js)|Medium| 563|[Binary Tree Tilt](./solutions/0563-binary-tree-tilt.js)|Easy| 564|[Find the Closest Palindrome](./solutions/0564-find-the-closest-palindrome.js)|Hard| 565|[Array Nesting](./solutions/0565-array-nesting.js)|Medium| 566|[Reshape the Matrix](./solutions/0566-reshape-the-matrix.js)|Easy| 567|[Permutation in String](./solutions/0567-permutation-in-string.js)|Medium| +568|[Maximum Vacation Days](./solutions/0568-maximum-vacation-days.js)|Hard| 572|[Subtree of Another Tree](./solutions/0572-subtree-of-another-tree.js)|Easy| +573|[Squirrel Simulation](./solutions/0573-squirrel-simulation.js)|Medium| 575|[Distribute Candies](./solutions/0575-distribute-candies.js)|Easy| 576|[Out of Boundary Paths](./solutions/0576-out-of-boundary-paths.js)|Medium| 581|[Shortest Unsorted Continuous Subarray](./solutions/0581-shortest-unsorted-continuous-subarray.js)|Medium| +582|[Kill Process](./solutions/0582-kill-process.js)|Medium| 583|[Delete Operation for Two Strings](./solutions/0583-delete-operation-for-two-strings.js)|Medium| 587|[Erect the Fence](./solutions/0587-erect-the-fence.js)|Hard| +588|[Design In-Memory File System](./solutions/0588-design-in-memory-file-system.js)|Hard| 589|[N-ary Tree Preorder Traversal](./solutions/0589-n-ary-tree-preorder-traversal.js)|Easy| 590|[N-ary Tree Postorder Traversal](./solutions/0590-n-ary-tree-postorder-traversal.js)|Easy| 591|[Tag Validator](./solutions/0591-tag-validator.js)|Hard| @@ -558,20 +571,25 @@ 598|[Range Addition II](./solutions/0598-range-addition-ii.js)|Easy| 599|[Minimum Index Sum of Two Lists](./solutions/0599-minimum-index-sum-of-two-lists.js)|Easy| 600|[Non-negative Integers without Consecutive Ones](./solutions/0600-non-negative-integers-without-consecutive-ones.js)|Hard| +604|[Design Compressed String Iterator](./solutions/0604-design-compressed-string-iterator.js)|Easy| 605|[Can Place Flowers](./solutions/0605-can-place-flowers.js)|Easy| 606|[Construct String from Binary Tree](./solutions/0606-construct-string-from-binary-tree.js)|Easy| 609|[Find Duplicate File in System](./solutions/0609-find-duplicate-file-in-system.js)|Medium| 611|[Valid Triangle Number](./solutions/0611-valid-triangle-number.js)|Medium| +616|[Add Bold Tag in String](./solutions/0616-add-bold-tag-in-string.js)|Medium| 617|[Merge Two Binary Trees](./solutions/0617-merge-two-binary-trees.js)|Easy| 621|[Task Scheduler](./solutions/0621-task-scheduler.js)|Medium| 622|[Design Circular Queue](./solutions/0622-design-circular-queue.js)|Medium| 623|[Add One Row to Tree](./solutions/0623-add-one-row-to-tree.js)|Medium| 624|[Maximum Distance in Arrays](./solutions/0624-maximum-distance-in-arrays.js)|Medium| +625|[Minimum Factorization](./solutions/0625-minimum-factorization.js)|Medium| 628|[Maximum Product of Three Numbers](./solutions/0628-maximum-product-of-three-numbers.js)|Easy| 629|[K Inverse Pairs Array](./solutions/0629-k-inverse-pairs-array.js)|Hard| 630|[Course Schedule III](./solutions/0630-course-schedule-iii.js)|Hard| +631|[Design Excel Sum Formula](./solutions/0631-design-excel-sum-formula.js)|Hard| 632|[Smallest Range Covering Elements from K Lists](./solutions/0632-smallest-range-covering-elements-from-k-lists.js)|Hard| 633|[Sum of Square Numbers](./solutions/0633-sum-of-square-numbers.js)|Medium| +634|[Find the Derangement of An Array](./solutions/0634-find-the-derangement-of-an-array.js)|Medium| 636|[Exclusive Time of Functions](./solutions/0636-exclusive-time-of-functions.js)|Medium| 637|[Average of Levels in Binary Tree](./solutions/0637-average-of-levels-in-binary-tree.js)|Easy| 638|[Shopping Offers](./solutions/0638-shopping-offers.js)|Medium| @@ -672,6 +690,7 @@ 754|[Reach a Number](./solutions/0754-reach-a-number.js)|Medium| 756|[Pyramid Transition Matrix](./solutions/0756-pyramid-transition-matrix.js)|Medium| 757|[Set Intersection Size At Least Two](./solutions/0757-set-intersection-size-at-least-two.js)|Hard| +758|[Bold Words in String](./solutions/0758-bold-words-in-string.js)|Medium| 761|[Special Binary String](./solutions/0761-special-binary-string.js)|Hard| 762|[Prime Number of Set Bits in Binary Representation](./solutions/0762-prime-number-of-set-bits-in-binary-representation.js)|Easy| 763|[Partition Labels](./solutions/0763-partition-labels.js)|Medium| @@ -2300,6 +2319,7 @@ 3405|[Count the Number of Arrays with K Matching Adjacent Elements](./solutions/3405-count-the-number-of-arrays-with-k-matching-adjacent-elements.js)|Hard| 3423|[Maximum Difference Between Adjacent Elements in a Circular Array](./solutions/3423-maximum-difference-between-adjacent-elements-in-a-circular-array.js)|Easy| 3442|[Maximum Difference Between Even and Odd Frequency I](./solutions/3442-maximum-difference-between-even-and-odd-frequency-i.js)|Easy| +3443|[Maximum Manhattan Distance After K Changes](./solutions/3443-maximum-manhattan-distance-after-k-changes.js)|Medium| 3445|[Maximum Difference Between Even and Odd Frequency II](./solutions/3445-maximum-difference-between-even-and-odd-frequency-ii.js)|Hard| 3452|[Sum of Good Numbers](./solutions/3452-sum-of-good-numbers.js)|Easy| 3461|[Check If Digits Are Equal in String After Operations I](./solutions/3461-check-if-digits-are-equal-in-string-after-operations-i.js)|Easy| diff --git a/solutions/0531-lonely-pixel-i.js b/solutions/0531-lonely-pixel-i.js new file mode 100644 index 0000000..41a2f95 --- /dev/null +++ b/solutions/0531-lonely-pixel-i.js @@ -0,0 +1,42 @@ +/** + * 531. Lonely Pixel I + * https://leetcode.com/problems/lonely-pixel-i/ + * Difficulty: Medium + * + * Given an m x n picture consisting of black 'B' and white 'W' pixels, return the number of + * black lonely pixels. + * + * A black lonely pixel is a character 'B' that located at a specific position where the same + * row and same column don't have any other black pixels. + */ + +/** + * @param {character[][]} picture + * @return {number} + */ +var findLonelyPixel = function(picture) { + const rows = picture.length; + const cols = picture[0].length; + const rowCounts = new Array(rows).fill(0); + const colCounts = new Array(cols).fill(0); + let result = 0; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + if (picture[row][col] === 'B') { + rowCounts[row]++; + colCounts[col]++; + } + } + } + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + if (picture[row][col] === 'B' && rowCounts[row] === 1 && colCounts[col] === 1) { + result++; + } + } + } + + return result; +}; diff --git a/solutions/0533-lonely-pixel-ii.js b/solutions/0533-lonely-pixel-ii.js new file mode 100644 index 0000000..c615bb3 --- /dev/null +++ b/solutions/0533-lonely-pixel-ii.js @@ -0,0 +1,57 @@ +/** + * 533. Lonely Pixel II + * https://leetcode.com/problems/lonely-pixel-ii/ + * Difficulty: Medium + * + * Given an m x n picture consisting of black 'B' and white 'W' pixels and an integer target, + * return the number of black lonely pixels. + * + * A black lonely pixel is a character 'B' that located at a specific position (r, c) where: + * - Row r and column c both contain exactly target black pixels. + * - For all rows that have a black pixel at column c, they should be exactly the same as row r. + */ + +/** + * @param {character[][]} picture + * @param {number} target + * @return {number} + */ +var findBlackPixel = function(picture, target) { + const rows = picture.length; + const cols = picture[0].length; + const rowCounts = new Array(rows).fill(0); + const colCounts = new Array(cols).fill(0); + const rowPatterns = new Map(); + + for (let row = 0; row < rows; row++) { + let blackCount = 0; + for (let col = 0; col < cols; col++) { + if (picture[row][col] === 'B') { + blackCount++; + colCounts[col]++; + } + } + rowCounts[row] = blackCount; + if (blackCount === target) { + const pattern = picture[row].join(''); + rowPatterns.set(pattern, (rowPatterns.get(pattern) || 0) + 1); + } + } + + let result = 0; + for (let col = 0; col < cols; col++) { + if (colCounts[col] === target) { + for (let row = 0; row < rows; row++) { + if (picture[row][col] === 'B' && rowCounts[row] === target) { + const pattern = picture[row].join(''); + if (rowPatterns.get(pattern) === target) { + result += target; + break; + } + } + } + } + } + + return result; +}; diff --git a/solutions/0536-construct-binary-tree-from-string.js b/solutions/0536-construct-binary-tree-from-string.js new file mode 100644 index 0000000..a6a9cb1 --- /dev/null +++ b/solutions/0536-construct-binary-tree-from-string.js @@ -0,0 +1,64 @@ +/** + * 536. Construct Binary Tree from String + * https://leetcode.com/problems/construct-binary-tree-from-string/ + * Difficulty: Medium + * + * You need to construct a binary tree from a string consisting of parenthesis and integers. + * + * The whole input represents a binary tree. It contains an integer followed by zero, one or + * two pairs of parenthesis. The integer represents the root's value and a pair of parenthesis + * contains a child binary tree with the same structure. + * + * You always start to construct the left child node of the parent first if it exists. + */ + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {string} s + * @return {TreeNode} + */ +var str2tree = function(s) { + if (!s) return null; + let index = 0; + return constructTree(); + + function parseNumber() { + const isNegative = s[index] === '-'; + if (isNegative) index++; + + let num = 0; + while (index < s.length && /[0-9]/.test(s[index])) { + num = num * 10 + parseInt(s[index]); + index++; + } + + return isNegative ? -num : num; + } + + function constructTree() { + if (index >= s.length) return null; + + const value = parseNumber(); + const node = new TreeNode(value); + if (index < s.length && s[index] === '(') { + index++; + node.left = constructTree(); + index++; + } + + if (index < s.length && s[index] === '(') { + index++; + node.right = constructTree(); + index++; + } + + return node; + } +}; diff --git a/solutions/0544-output-contest-matches.js b/solutions/0544-output-contest-matches.js new file mode 100644 index 0000000..e1392dd --- /dev/null +++ b/solutions/0544-output-contest-matches.js @@ -0,0 +1,39 @@ +/** + * 544. Output Contest Matches + * https://leetcode.com/problems/output-contest-matches/ + * Difficulty: Medium + * + * During the NBA playoffs, we always set the rather strong team to play with the rather weak + * team, like making the rank 1 team play with the rank nth team, which is a good strategy to + * make the contest more interesting. + * + * Given n teams, return their final contest matches in the form of a string. + * + * The n teams are labeled from 1 to n, which represents their initial rank (i.e., Rank 1 is + * the strongest team and Rank n is the weakest team). + * + * We will use parentheses '(', and ')' and commas ',' to represent the contest team pairing. + * We use the parentheses for pairing and the commas for partition. During the pairing process + * in each round, you always need to follow the strategy of making the rather strong one pair + * with the rather weak one. + */ + +/** + * @param {number} n + * @return {string} + */ +var findContestMatch = function(n) { + const teams = Array.from({ length: n }, (_, i) => (i + 1).toString()); + return pairTeams(teams); + + function pairTeams(arr) { + if (arr.length === 1) return arr[0]; + + const nextRound = []; + for (let i = 0; i < arr.length / 2; i++) { + nextRound.push(`(${arr[i]},${arr[arr.length - 1 - i]})`); + } + + return pairTeams(nextRound); + } +}; diff --git a/solutions/0545-boundary-of-binary-tree.js b/solutions/0545-boundary-of-binary-tree.js new file mode 100644 index 0000000..eabf126 --- /dev/null +++ b/solutions/0545-boundary-of-binary-tree.js @@ -0,0 +1,84 @@ +/** + * 545. Boundary of Binary Tree + * https://leetcode.com/problems/boundary-of-binary-tree/ + * Difficulty: Medium + * + * The boundary of a binary tree is the concatenation of the root, the left boundary, the leaves + * ordered from left-to-right, and the reverse order of the right boundary. + * + * The left boundary is the set of nodes defined by the following: + * - The root node's left child is in the left boundary. If the root does not have a left child, + * then the left boundary is empty. + * - If a node in the left boundary and has a left child, then the left child is in the left + * boundary. + * - If a node is in the left boundary, has no left child, but has a right child, then the right + * child is in the left boundary. + * - The leftmost leaf is not in the left boundary. + * + * The right boundary is similar to the left boundary, except it is the right side of the root's + * right subtree. Again, the leaf is not part of the right boundary, and the right boundary is + * empty if the root does not have a right child. + * + * The leaves are nodes that do not have any children. For this problem, the root is not a leaf. + * + * Given the root of a binary tree, return the values of its boundary. + */ + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var boundaryOfBinaryTree = function(root) { + if (!root) return []; + if (!root.left && !root.right) return [root.val]; + + const result = [root.val]; + + collectLeftBoundary(root.left); + collectLeaves(root); + collectRightBoundary(root.right); + + return result; + + function isLeaf(node) { + return node && !node.left && !node.right; + } + + function collectLeftBoundary(node) { + if (!node || isLeaf(node)) return; + result.push(node.val); + if (node.left) { + collectLeftBoundary(node.left); + } else { + collectLeftBoundary(node.right); + } + } + + function collectLeaves(node) { + if (!node) return; + if (isLeaf(node)) { + result.push(node.val); + return; + } + collectLeaves(node.left); + collectLeaves(node.right); + } + + function collectRightBoundary(node) { + if (!node || isLeaf(node)) return; + if (node.right) { + collectRightBoundary(node.right); + } else { + collectRightBoundary(node.left); + } + result.push(node.val); + } +}; diff --git a/solutions/0548-split-array-with-equal-sum.js b/solutions/0548-split-array-with-equal-sum.js new file mode 100644 index 0000000..672843b --- /dev/null +++ b/solutions/0548-split-array-with-equal-sum.js @@ -0,0 +1,48 @@ +/** + * 548. Split Array with Equal Sum + * https://leetcode.com/problems/split-array-with-equal-sum/ + * Difficulty: Hard + * + * Given an integer array nums of length n, return true if there is a triplet (i, j, k) which + * satisfies the following conditions: + * - 0 < i, i + 1 < j, j + 1 < k < n - 1 + * - The sum of subarrays (0, i - 1), (i + 1, j - 1), (j + 1, k - 1) and (k + 1, n - 1) is equal. + * + * A subarray (l, r) represents a slice of the original array starting from the element + * indexed l to the element indexed r. + */ + +/** + * @param {number[]} nums + * @return {boolean} + */ +var splitArray = function(nums) { + const length = nums.length; + if (length < 7) return false; + + const prefixSums = new Array(length + 1).fill(0); + for (let i = 0; i < length; i++) { + prefixSums[i + 1] = prefixSums[i] + nums[i]; + } + + const getSum = (start, end) => prefixSums[end + 1] - prefixSums[start]; + + for (let j = 3; j < length - 3; j++) { + const seenSums = new Set(); + for (let i = 1; i < j - 1; i++) { + const firstSum = getSum(0, i - 1); + const secondSum = getSum(i + 1, j - 1); + if (firstSum === secondSum) { + seenSums.add(firstSum); + } + } + for (let k = j + 2; k < length - 1; k++) { + const thirdSum = getSum(j + 1, k - 1); + const fourthSum = getSum(k + 1, length - 1); + if (thirdSum === fourthSum && seenSums.has(thirdSum)) { + return true; + } + } + } + return false; +}; diff --git a/solutions/0549-binary-tree-longest-consecutive-sequence-ii.js b/solutions/0549-binary-tree-longest-consecutive-sequence-ii.js new file mode 100644 index 0000000..158a829 --- /dev/null +++ b/solutions/0549-binary-tree-longest-consecutive-sequence-ii.js @@ -0,0 +1,61 @@ +/** + * 549. Binary Tree Longest Consecutive Sequence II + * https://leetcode.com/problems/binary-tree-longest-consecutive-sequence-ii/ + * Difficulty: Medium + * + * Given the root of a binary tree, return the length of the longest consecutive path in the tree. + * + * A consecutive path is a path where the values of the consecutive nodes in the path differ by one. + * This path can be either increasing or decreasing. + * - For example, [1,2,3,4] and [4,3,2,1] are both considered valid, but the path [1,2,4,3] is + * not valid. + * + * On the other hand, the path can be in the child-Parent-child order, where not necessarily be + * parent-child order. + */ + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var longestConsecutive = function(root) { + let maxLength = 0; + traverse(root); + return maxLength; + + function traverse(node) { + if (!node) return [0, 0]; + + let inc = 1; + let dec = 1; + + if (node.left) { + const [leftInc, leftDec] = traverse(node.left); + if (node.val === node.left.val + 1) { + dec = Math.max(dec, leftDec + 1); + } else if (node.val === node.left.val - 1) { + inc = Math.max(inc, leftInc + 1); + } + } + + if (node.right) { + const [rightInc, rightDec] = traverse(node.right); + if (node.val === node.right.val + 1) { + dec = Math.max(dec, rightDec + 1); + } else if (node.val === node.right.val - 1) { + inc = Math.max(inc, rightInc + 1); + } + } + + maxLength = Math.max(maxLength, inc + dec - 1); + return [inc, dec]; + } +}; diff --git a/solutions/0555-split-concatenated-strings.js b/solutions/0555-split-concatenated-strings.js new file mode 100644 index 0000000..9f353e1 --- /dev/null +++ b/solutions/0555-split-concatenated-strings.js @@ -0,0 +1,50 @@ +/** + * 555. Split Concatenated Strings + * https://leetcode.com/problems/split-concatenated-strings/ + * Difficulty: Medium + * + * You are given an array of strings strs. You could concatenate these strings together into a + * loop, where for each string, you could choose to reverse it or not. Among all the possible loops + * + * Return the lexicographically largest string after cutting the loop, which will make the looped + * string into a regular one. + * + * + * Specifically, to find the lexicographically largest string, you need to experience two phases: + * 1. Concatenate all the strings into a loop, where you can reverse some strings or not and connect + * them in the same order as given. + * 2. Cut and make one breakpoint in any place of the loop, which will make the looped string into + * a regular one starting from the character at the cutpoint. + * + * And your job is to find the lexicographically largest one among all the possible regular strings. + */ + +/** + * @param {string[]} strs + * @return {string} + */ +var splitLoopedString = function(strs) { + const normalized = strs.map(str => { + const reversed = str.split('').reverse().join(''); + return str > reversed ? str : reversed; + }); + + let result = ''; + for (let i = 0; i < normalized.length; i++) { + const current = normalized[i]; + const prefix = normalized.slice(i + 1).join('') + normalized.slice(0, i).join(''); + const original = current; + const reversed = current.split('').reverse().join(''); + + for (const str of [original, reversed]) { + for (let j = 0; j <= str.length; j++) { + const candidate = str.slice(j) + prefix + str.slice(0, j); + if (candidate > result) { + result = candidate; + } + } + } + } + + return result; +}; diff --git a/solutions/0562-longest-line-of-consecutive-one-in-matrix.js b/solutions/0562-longest-line-of-consecutive-one-in-matrix.js new file mode 100644 index 0000000..601f020 --- /dev/null +++ b/solutions/0562-longest-line-of-consecutive-one-in-matrix.js @@ -0,0 +1,35 @@ +/** + * 562. Longest Line of Consecutive One in Matrix + * https://leetcode.com/problems/longest-line-of-consecutive-one-in-matrix/ + * Difficulty: Medium + * + * Given an m x n binary matrix mat, return the length of the longest line of consecutive one + * in the matrix. + * + * The line could be horizontal, vertical, diagonal, or anti-diagonal. + */ + +/** + * @param {number[][]} mat + * @return {number} + */ +var longestLine = function(mat) { + const rows = mat.length; + const cols = mat[0].length; + const dp = new Array(rows).fill().map(() => new Array(cols).fill().map(() => [0, 0, 0, 0])); + let maxLength = 0; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (mat[i][j] === 1) { + dp[i][j][0] = j > 0 ? dp[i][j - 1][0] + 1 : 1; + dp[i][j][1] = i > 0 ? dp[i - 1][j][1] + 1 : 1; + dp[i][j][2] = (i > 0 && j > 0) ? dp[i - 1][j - 1][2] + 1 : 1; + dp[i][j][3] = (i > 0 && j < cols - 1) ? dp[i - 1][j + 1][3] + 1 : 1; + maxLength = Math.max(maxLength, ...dp[i][j]); + } + } + } + + return maxLength; +}; diff --git a/solutions/0568-maximum-vacation-days.js b/solutions/0568-maximum-vacation-days.js new file mode 100644 index 0000000..3ad9a27 --- /dev/null +++ b/solutions/0568-maximum-vacation-days.js @@ -0,0 +1,61 @@ +/** + * 568. Maximum Vacation Days + * https://leetcode.com/problems/maximum-vacation-days/ + * Difficulty: Hard + * + * LeetCode wants to give one of its best employees the option to travel among n cities to collect + * algorithm problems. But all work and no play makes Jack a dull boy, you could take vacations in + * some particular cities and weeks. Your job is to schedule the traveling to maximize the number + * of vacation days you could take, but there are certain rules and restrictions you need to follow. + * + * Rules and restrictions: + * 1. You can only travel among n cities, represented by indexes from 0 to n - 1. Initially, you + * are in the city indexed 0 on Monday. + * 2. The cities are connected by flights. The flights are represented as an n x n matrix (not + * necessarily symmetrical), called flights representing the airline status from the city i to + * the city j. If there is no flight from the city i to the city j, flights[i][j] == 0; + * Otherwise, flights[i][j] == 1. Also, flights[i][i] == 0 for all i. + * 3. You totally have k weeks (each week has seven days) to travel. You can only take flights at + * most once per day and can only take flights on each week's Monday morning. Since flight time + * is so short, we do not consider the impact of flight time. + * 4. For each city, you can only have restricted vacation days in different weeks, given an n x k + * matrix called days representing this relationship. For the value of days[i][j], it represents + * the maximum days you could take a vacation in the city i in the week j. + * 5. You could stay in a city beyond the number of vacation days, but you should work on the extra + * days, which will not be counted as vacation days. + * 6. If you fly from city A to city B and take the vacation on that day, the deduction towards + * vacation days will count towards the vacation days of city B in that week. + * 7. We do not consider the impact of flight hours on the calculation of vacation days. + * + * Given the two matrices flights and days, return the maximum vacation days you could take during + * k weeks. + */ + +/** + * @param {number[][]} flights + * @param {number[][]} days + * @return {number} + */ +var maxVacationDays = function(flights, days) { + const cities = flights.length; + const weeks = days[0].length; + const dp = new Array(weeks + 1).fill().map(() => new Array(cities).fill(-Infinity)); + + dp[0][0] = 0; + + for (let week = 1; week <= weeks; week++) { + for (let currCity = 0; currCity < cities; currCity++) { + for (let prevCity = 0; prevCity < cities; prevCity++) { + if (dp[week - 1][prevCity] !== -Infinity + && (flights[prevCity][currCity] || prevCity === currCity)) { + dp[week][currCity] = Math.max( + dp[week][currCity], + dp[week - 1][prevCity] + days[currCity][week - 1] + ); + } + } + } + } + + return Math.max(0, ...dp[weeks]); +}; diff --git a/solutions/0573-squirrel-simulation.js b/solutions/0573-squirrel-simulation.js new file mode 100644 index 0000000..0e6c74c --- /dev/null +++ b/solutions/0573-squirrel-simulation.js @@ -0,0 +1,44 @@ +/** + * 573. Squirrel Simulation + * https://leetcode.com/problems/squirrel-simulation/ + * Difficulty: Medium + * + * You are given two integers height and width representing a garden of size height x width. + * You are also given: + * - an array tree where tree = [treer, treec] is the position of the tree in the garden, + * - an array squirrel where squirrel = [squirrelr, squirrelc] is the position of the squirrel + * in the garden, + * - and an array nuts where nuts[i] = [nutir, nutic] is the position of the ith nut in the garden. + * + * The squirrel can only take at most one nut at one time and can move in four directions: up, down, + * left, and right, to the adjacent cell. + * + * Return the minimal distance for the squirrel to collect all the nuts and put them under the tree + * one by one. + * + * The distance is the number of moves. + */ + +/** + * @param {number} height + * @param {number} width + * @param {number[]} tree + * @param {number[]} squirrel + * @param {number[][]} nuts + * @return {number} + */ +var minDistance = function(height, width, tree, squirrel, nuts) { + const helper = (p1, p2) => Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]); + + let totalDistance = 0; + let maxSaving = -Infinity; + + for (const nut of nuts) { + const nutToTree = helper(nut, tree); + totalDistance += 2 * nutToTree; + const squirrelToNut = helper(squirrel, nut); + maxSaving = Math.max(maxSaving, nutToTree - squirrelToNut); + } + + return totalDistance - maxSaving; +}; diff --git a/solutions/0582-kill-process.js b/solutions/0582-kill-process.js new file mode 100644 index 0000000..bc550c4 --- /dev/null +++ b/solutions/0582-kill-process.js @@ -0,0 +1,46 @@ +/** + * 582. Kill Process + * https://leetcode.com/problems/kill-process/ + * Difficulty: Medium + * + * You have n processes forming a rooted tree structure. You are given two integer arrays pid + * and ppid, where pid[i] is the ID of the ith process and ppid[i] is the ID of the ith process's + * parent process. + * + * Each process has only one parent process but may have multiple children processes. Only one + * process has ppid[i] = 0, which means this process has no parent process (the root of the tree). + * + * When a process is killed, all of its children processes will also be killed. + * + * Given an integer kill representing the ID of a process you want to kill, return a list of the + * IDs of the processes that will be killed. You may return the answer in any order. + */ + +/** + * @param {number[]} pid + * @param {number[]} ppid + * @param {number} kill + * @return {number[]} + */ +var killProcess = function(pid, ppid, kill) { + const map = new Map(); + for (let i = 0; i < ppid.length; i++) { + if (!map.has(ppid[i])) { + map.set(ppid[i], []); + } + map.get(ppid[i]).push(pid[i]); + } + + const result = []; + traverse(kill); + return result; + + function traverse(process) { + result.push(process); + if (map.has(process)) { + for (const child of map.get(process)) { + traverse(child); + } + } + } +}; diff --git a/solutions/0588-design-in-memory-file-system.js b/solutions/0588-design-in-memory-file-system.js new file mode 100644 index 0000000..a3e2e20 --- /dev/null +++ b/solutions/0588-design-in-memory-file-system.js @@ -0,0 +1,97 @@ +/** + * 588. Design In-Memory File System + * https://leetcode.com/problems/design-in-memory-file-system/ + * Difficulty: Hard + * + * Design a data structure that simulates an in-memory file system. + * + * Implement the FileSystem class: + * - FileSystem() Initializes the object of the system. + * - List ls(String path) + * - If path is a file path, returns a list that only contains this file's name. + * - If path is a directory path, returns the list of file and directory names in this directory. + * - The answer should in lexicographic order. + * - void mkdir(String path) Makes a new directory according to the given path. The given directory + * path does not exist. If the middle directories in the path do not exist, you should create + * them as well. + * - void addContentToFile(String filePath, String content) + * - If filePath does not exist, creates that file containing given content. + * - If filePath already exists, appends the given content to original content. + * - String readContentFromFile(String filePath) Returns the content in the file at filePath. + */ + +var FileSystem = function() { + this.root = new Map(); +}; + +/** + * @param {string} path + * @return {string[]} + */ +FileSystem.prototype.ls = function(path) { + const parts = path === '/' ? [] : path.split('/').slice(1); + let current = this.root; + + for (const part of parts) { + current = current.get(part); + } + + if (typeof current === 'string') { + return [parts[parts.length - 1]]; + } + + return Array.from(current.keys()).sort(); +}; + +/** + * @param {string} path + * @return {void} + */ +FileSystem.prototype.mkdir = function(path) { + const parts = path.split('/').slice(1); + let current = this.root; + + for (const part of parts) { + if (!current.has(part)) { + current.set(part, new Map()); + } + current = current.get(part); + } +}; + +/** + * @param {string} filePath + * @param {string} content + * @return {void} + */ +FileSystem.prototype.addContentToFile = function(filePath, content) { + const parts = filePath.split('/').slice(1); + const fileName = parts.pop(); + let current = this.root; + + for (const part of parts) { + if (!current.has(part)) { + current.set(part, new Map()); + } + current = current.get(part); + } + + const existingContent = current.get(fileName) || ''; + current.set(fileName, existingContent + content); +}; + +/** + * @param {string} filePath + * @return {string} + */ +FileSystem.prototype.readContentFromFile = function(filePath) { + const parts = filePath.split('/').slice(1); + const fileName = parts.pop(); + let current = this.root; + + for (const part of parts) { + current = current.get(part); + } + + return current.get(fileName); +}; diff --git a/solutions/0604-design-compressed-string-iterator.js b/solutions/0604-design-compressed-string-iterator.js new file mode 100644 index 0000000..e79f8be --- /dev/null +++ b/solutions/0604-design-compressed-string-iterator.js @@ -0,0 +1,65 @@ +/** + * 604. Design Compressed String Iterator + * https://leetcode.com/problems/design-compressed-string-iterator/ + * Difficulty: Easy + * + * Design and implement a data structure for a compressed string iterator. The given compressed + * string will be in the form of each letter followed by a positive integer representing the + * number of this letter existing in the original uncompressed string. + * + * Implement the StringIterator class: + * - next() Returns the next character if the original string still has uncompressed characters, + * otherwise returns a white space. + * - hasNext() Returns true if there is any letter needs to be uncompressed in the original string, + * otherwise returns false. + */ + +/** + * @param {string} compressedString + */ +var StringIterator = function(compressedString) { + this.segments = []; + this.currentIndex = 0; + this.currentCount = 0; + + let i = 0; + while (i < compressedString.length) { + const character = compressedString[i]; + i++; + let count = ''; + while (i < compressedString.length && /\d/.test(compressedString[i])) { + count += compressedString[i]; + i++; + } + this.segments.push([character, parseInt(count)]); + } +}; + +/** + * @return {character} + */ +StringIterator.prototype.next = function() { + if (!this.hasNext()) { + return ' '; + } + + if (this.currentCount === 0) { + this.currentCount = this.segments[this.currentIndex][1]; + } + + const character = this.segments[this.currentIndex][0]; + this.currentCount--; + + if (this.currentCount === 0) { + this.currentIndex++; + } + + return character; +}; + +/** + * @return {boolean} + */ +StringIterator.prototype.hasNext = function() { + return this.currentIndex < this.segments.length; +}; diff --git a/solutions/0616-add-bold-tag-in-string.js b/solutions/0616-add-bold-tag-in-string.js new file mode 100644 index 0000000..563c80c --- /dev/null +++ b/solutions/0616-add-bold-tag-in-string.js @@ -0,0 +1,61 @@ +/** + * 616. Add Bold Tag in String + * https://leetcode.com/problems/add-bold-tag-in-string/ + * Difficulty: Medium + * + * You are given a string s and an array of strings words. + * + * You should add a closed pair of bold tag and to wrap the substrings in s that + * exist in words. + * - If two such substrings overlap, you should wrap them together with only one pair of + * closed bold-tag. + * - If two substrings wrapped by bold tags are consecutive, you should combine them. + * + * Return s after adding the bold tags. + */ + +/** + * @param {string} s + * @param {string[]} words + * @return {string} + */ +var addBoldTag = function(s, words) { + const boldIntervals = []; + + for (const word of words) { + let start = s.indexOf(word); + while (start !== -1) { + boldIntervals.push([start, start + word.length]); + start = s.indexOf(word, start + 1); + } + } + + if (!boldIntervals.length) return s; + + boldIntervals.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + const mergedIntervals = []; + let [currentStart, currentEnd] = boldIntervals[0]; + + for (let i = 1; i < boldIntervals.length; i++) { + const [nextStart, nextEnd] = boldIntervals[i]; + if (nextStart <= currentEnd) { + currentEnd = Math.max(currentEnd, nextEnd); + } else { + mergedIntervals.push([currentStart, currentEnd]); + [currentStart, currentEnd] = [nextStart, nextEnd]; + } + } + mergedIntervals.push([currentStart, currentEnd]); + + let result = ''; + let lastEnd = 0; + for (const [start, end] of mergedIntervals) { + result += s.slice(lastEnd, start) + '' + s.slice(start, end) + ''; + lastEnd = end; + } + + result += s.slice(lastEnd); + + return result; +}; diff --git a/solutions/0625-minimum-factorization.js b/solutions/0625-minimum-factorization.js new file mode 100644 index 0000000..8001929 --- /dev/null +++ b/solutions/0625-minimum-factorization.js @@ -0,0 +1,35 @@ +/** + * 625. Minimum Factorization + * https://leetcode.com/problems/minimum-factorization/ + * Difficulty: Medium + * + * Given a positive integer num, return the smallest positive integer x whose multiplication + * of each digit equals num. If there is no answer or the answer is not fit in 32-bit signed + * integer, return 0. + */ + +/** + * @param {number} num + * @return {number} + */ +var smallestFactorization = function(num) { + if (num < 2) return num; + + const digits = []; + for (let divisor = 9; divisor > 1; divisor--) { + while (num % divisor === 0) { + digits.push(divisor); + num /= divisor; + } + } + + if (num > 1) return 0; + + let result = 0; + for (const digit of digits.reverse()) { + result = result * 10 + digit; + if (result > 2**31 - 1) return 0; + } + + return result; +}; diff --git a/solutions/0631-design-excel-sum-formula.js b/solutions/0631-design-excel-sum-formula.js new file mode 100644 index 0000000..83af152 --- /dev/null +++ b/solutions/0631-design-excel-sum-formula.js @@ -0,0 +1,141 @@ +/** + * 631. Design Excel Sum Formula + * https://leetcode.com/problems/design-excel-sum-formula/ + * Difficulty: Hard + * + * Design the basic function of Excel and implement the function of the sum formula. + * + * Implement the Excel class: + * - Excel(int height, char width) Initializes the object with the height and the width + * of the sheet. The sheet is an integer matrix mat of size height x width with the row + * index in the range [1, height] and the column index in the range ['A', width]. All the + * values should be zero initially. + * - void set(int row, char column, int val) Changes the value at mat[row][column] to be val. + * - int get(int row, char column) Returns the value at mat[row][column]. + * - int sum(int row, char column, List numbers) Sets the value at mat[row][column] to + * be the sum of cells represented by numbers and returns the value at mat[row][column]. + * This sum formula should exist until this cell is overlapped by another value or another + * sum formula. numbers[i] could be on the format: + * - "ColRow" that represents a single cell. + * - For example, "F7" represents the cell mat[7]['F']. + * - "ColRow1:ColRow2" that represents a range of cells. The range will always be a rectangle + * where "ColRow1" represent the position of the top-left cell, and "ColRow2" represents the + * position of the bottom-right cell. + * - For example, "B3:F7" represents the cells mat[i][j] for 3 <= i <= 7 and 'B' <= j <= 'F'. + * + * Note: You could assume that there will not be any circular sum reference. + * + * For example, mat[1]['A'] == sum(1, "B") and mat[1]['B'] == sum(1, "A"). + */ + +/** + * @param {number} height + * @param {character} width + */ +var Excel = function(height, width) { + this.height = height; + this.width = width.charCodeAt(0) - 64; + this.matrix = new Array(height).fill().map(() => new Array(this.width).fill(0)); + this.formulas = new Map(); + this.dependencies = new Map(); +}; + +/** + * @param {number} row + * @param {character} column + * @param {number} val + * @return {void} + */ +Excel.prototype.set = function(row, column, val) { + const col = column.charCodeAt(0) - 65; + const key = `${row}:${column}`; + this.matrix[row - 1][col] = val; + if (this.formulas.has(key)) { + this.formulas.delete(key); + this.dependencies.get(key)?.delete(key); + } + this.updateDependents(row, column); +}; + +/** + * @param {number} row + * @param {character} column + * @return {number} + */ +Excel.prototype.get = function(row, column) { + const col = column.charCodeAt(0) - 65; + return this.matrix[row - 1][col]; +}; + +/** + * @param {number} row + * @param {character} column + * @param {string[]} numbers + * @return {number} + */ +Excel.prototype.sum = function(row, column, numbers) { + const col = column.charCodeAt(0) - 65; + const cellKey = `${row}:${column}`; + const cells = []; + + for (const num of numbers) { + if (num.includes(':')) { + const [start, end] = num.split(':'); + const startCol = start[0].charCodeAt(0) - 65; + const startRow = parseInt(start.slice(1)) - 1; + const endCol = end[0].charCodeAt(0) - 65; + const endRow = parseInt(end.slice(1)) - 1; + + for (let r = startRow; r <= endRow; r++) { + for (let c = startCol; c <= endCol; c++) { + cells.push([r, c]); + } + } + } else { + const c = num[0].charCodeAt(0) - 65; + const r = parseInt(num.slice(1)) - 1; + cells.push([r, c]); + } + } + + if (this.formulas.has(cellKey)) { + for (const [r, c] of this.formulas.get(cellKey)) { + const depKey = `${r + 1}:${String.fromCharCode(c + 65)}`; + this.dependencies.get(depKey)?.delete(cellKey); + } + } + + this.formulas.set(cellKey, cells); + for (const [r, c] of cells) { + const depKey = `${r + 1}:${String.fromCharCode(c + 65)}`; + if (!this.dependencies.has(depKey)) { + this.dependencies.set(depKey, new Set()); + } + this.dependencies.get(depKey).add(cellKey); + } + + const total = this.calculateSum(cells); + this.matrix[row - 1][col] = total; + this.updateDependents(row, column); + return total; +}; + +Excel.prototype.calculateSum = function(cells) { + return cells.reduce((sum, [r, c]) => sum + this.matrix[r][c], 0); +}; + +Excel.prototype.updateDependents = function(row, column) { + const key = `${row}:${column}`; + if (!this.dependencies.has(key)) return; + + for (const depKey of this.dependencies.get(key)) { + const [depRow, depCol] = depKey.split(':'); + const col = depCol.charCodeAt(0) - 65; + const cells = this.formulas.get(depKey); + if (cells) { + const newSum = this.calculateSum(cells); + this.matrix[parseInt(depRow) - 1][col] = newSum; + this.updateDependents(parseInt(depRow), depCol); + } + } +}; diff --git a/solutions/0634-find-the-derangement-of-an-array.js b/solutions/0634-find-the-derangement-of-an-array.js new file mode 100644 index 0000000..7efca72 --- /dev/null +++ b/solutions/0634-find-the-derangement-of-an-array.js @@ -0,0 +1,32 @@ +/** + * 634. Find the Derangement of An Array + * https://leetcode.com/problems/find-the-derangement-of-an-array/ + * Difficulty: Medium + * + * In combinatorial mathematics, a derangement is a permutation of the elements of a set, + * such that no element appears in its original position. + * + * You are given an integer n. There is originally an array consisting of n integers from + * 1 to n in ascending order, return the number of derangements it can generate. Since the + * answer may be huge, return it modulo 109 + 7. + */ + +/** + * @param {number} n + * @return {number} + */ +var findDerangement = function(n) { + const MOD = 1e9 + 7; + if (n === 1) return 0; + if (n === 2) return 1; + + let prev2 = 0; + let prev1 = 1; + for (let i = 3; i <= n; i++) { + const current = ((i - 1) * (prev1 + prev2)) % MOD; + prev2 = prev1; + prev1 = current; + } + + return prev1; +}; diff --git a/solutions/0758-bold-words-in-string.js b/solutions/0758-bold-words-in-string.js new file mode 100644 index 0000000..5187a17 --- /dev/null +++ b/solutions/0758-bold-words-in-string.js @@ -0,0 +1,57 @@ +/** + * 758. Bold Words in String + * https://leetcode.com/problems/bold-words-in-string/ + * Difficulty: Medium + * + * Given an array of keywords words and a string s, make all appearances of all keywords words[i] + * in s bold. Any letters between and tags become bold. + * + * Return s after adding the bold tags. The returned string should use the least number of tags + * possible, and the tags should form a valid combination. + */ + +/** + * @param {string[]} words + * @param {string} s + * @return {string} + */ +var boldWords = function(words, s) { + const boldIntervals = []; + + for (const word of words) { + let start = s.indexOf(word); + while (start !== -1) { + boldIntervals.push([start, start + word.length]); + start = s.indexOf(word, start + 1); + } + } + + if (!boldIntervals.length) return s; + + boldIntervals.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + const mergedIntervals = []; + let [currentStart, currentEnd] = boldIntervals[0]; + + for (let i = 1; i < boldIntervals.length; i++) { + const [nextStart, nextEnd] = boldIntervals[i]; + if (nextStart <= currentEnd) { + currentEnd = Math.max(currentEnd, nextEnd); + } else { + mergedIntervals.push([currentStart, currentEnd]); + [currentStart, currentEnd] = [nextStart, nextEnd]; + } + } + mergedIntervals.push([currentStart, currentEnd]); + + let result = ''; + let lastEnd = 0; + for (const [start, end] of mergedIntervals) { + result += s.slice(lastEnd, start) + '' + s.slice(start, end) + ''; + lastEnd = end; + } + + result += s.slice(lastEnd); + + return result; +}; diff --git a/solutions/3443-maximum-manhattan-distance-after-k-changes.js b/solutions/3443-maximum-manhattan-distance-after-k-changes.js new file mode 100644 index 0000000..21f7f3c --- /dev/null +++ b/solutions/3443-maximum-manhattan-distance-after-k-changes.js @@ -0,0 +1,51 @@ +/** + * 3443. Maximum Manhattan Distance After K Changes + * https://leetcode.com/problems/maximum-manhattan-distance-after-k-changes/ + * Difficulty: Medium + * + * You are given a string s consisting of the characters 'N', 'S', 'E', and 'W', where s[i] + * indicates movements in an infinite grid: + * - 'N' : Move north by 1 unit. + * - 'S' : Move south by 1 unit. + * - 'E' : Move east by 1 unit. + * - 'W' : Move west by 1 unit. + * + * Initially, you are at the origin (0, 0). You can change at most k characters to any of the + * four directions. + * + * Find the maximum Manhattan distance from the origin that can be achieved at any time while + * performing the movements in order. + * + * The Manhattan Distance between two cells (xi, yi) and (xj, yj) is |xi - xj| + |yi - yj|. + */ + +/** + * @param {string} s + * @param {number} k + * @return {number} + */ +var maxDistance = function(s, k) { + const directions = { + 'N': [0, 1], + 'S': [0, -1], + 'E': [1, 0], + 'W': [-1, 0] + }; + + let x = 0; + let y = 0; + let result = 0; + + for (let i = 0; i < s.length; i++) { + const [dx, dy] = directions[s[i]]; + x += dx; + y += dy; + + const currentDistance = Math.abs(x) + Math.abs(y); + const maxPossibleExtra = Math.min(2 * k, i + 1 - currentDistance); + + result = Math.max(result, currentDistance + maxPossibleExtra); + } + + return result; +};