diff --git a/articles/all-possible-full-binary-trees.md b/articles/all-possible-full-binary-trees.md new file mode 100644 index 000000000..1e75b0293 --- /dev/null +++ b/articles/all-possible-full-binary-trees.md @@ -0,0 +1,693 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + def backtrack(n): + if n == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for l in range(n): + r = n - 1 - l + leftTrees, rightTrees = backtrack(l), backtrack(r) + + for t1 in leftTrees: + for t2 in rightTrees: + res.append(TreeNode(0, t1, t2)) + return res + + return backtrack(n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + return backtrack(n); + } + + private List backtrack(int n) { + if (n == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + List leftTrees = backtrack(l); + List rightTrees = backtrack(r); + + for (TreeNode t1 : leftTrees) { + for (TreeNode t2 : rightTrees) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + return backtrack(n); + } + +private: + vector backtrack(int n) { + if (n == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + vector leftTrees = backtrack(l); + vector rightTrees = backtrack(r); + + for (auto& t1 : leftTrees) { + for (auto& t2 : rightTrees) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + const backtrack = (n) => { + if (n === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let l = 0; l < n; l++) { + let r = n - 1 - l; + let leftTrees = backtrack(l); + let rightTrees = backtrack(r); + + for (let t1 of leftTrees) { + for (let t2 of rightTrees) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + return res; + }; + + return backtrack(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 2. Recursion (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for left in range(1, n, 2): + leftSubTree = self.allPossibleFBT(left) + rightSubTree = self.allPossibleFBT(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + root = TreeNode(0, l, r) + res.append(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = allPossibleFBT(left); + List rightSubTree = allPossibleFBT(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + TreeNode root = new TreeNode(0, l, r); + res.add(root); + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = allPossibleFBT(left); + vector rightSubTree = allPossibleFBT(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + TreeNode* root = new TreeNode(0, l, r); + res.push_back(root); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = this.allPossibleFBT(left); + let rightSubTree = this.allPossibleFBT(n - 1 - left); + for (let l of leftSubTree) { + for (let r of rightSubTree) { + let root = new TreeNode(0, l, r); + res.push(root); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + dp = {} + + def dfs(n): + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + if n in dp: + return dp[n] + + res = [] + for left in range(1, n, 2): + leftSubTree = dfs(left) + rightSubTree = dfs(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + res.append(TreeNode(0, l, r)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List[] dp; + + public List allPossibleFBT(int n) { + dp = new ArrayList[n + 1]; + return dfs(n); + } + + private List dfs(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + if (dp[n] != null) { + return dp[n]; + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = dfs(left); + List rightSubTree = dfs(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + res.add(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + vector> dp; + +public: + vector allPossibleFBT(int n) { + dp.resize(n + 1); + return dfs(n); + } + + vector dfs(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + if (!dp[n].empty()) { + return dp[n]; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = dfs(left); + vector rightSubTree = dfs(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + res.push_back(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + let dp = new Array(n + 1); + + const dfs = (n) => { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + if (dp[n]) { + return dp[n]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = dfs(left); + let rightSubTree = dfs(n - 1 - left); + for (let t1 of leftSubTree) { + for (let t2 of rightSubTree) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + + return (dp[n] = res); + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + + dp = [[] for _ in range(n + 1)] + dp[1] = [TreeNode(0)] + + for nodes in range(3, n + 1, 2): + res = [] + for left in range(1, nodes, 2): + right = nodes - 1 - left + for t1 in dp[left]: + for t2 in dp[right]: + res.append(TreeNode(0, t1, t2)) + dp[nodes] = res + + return dp[n] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + + List[] dp = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new ArrayList<>(); + } + dp[1].add(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + List res = new ArrayList<>(); + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (TreeNode t1 : dp[left]) { + for (TreeNode t2 : dp[right]) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + + vector> dp(n + 1); + dp[1].push_back(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + vector res; + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (auto& t1 : dp[left]) { + for (auto& t2 : dp[right]) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + + let dp = Array.from({ length: n + 1 }, () => []); + dp[1] = [new TreeNode(0)]; + + for (let nodes = 3; nodes <= n; nodes += 2) { + let res = []; + for (let left = 1; left < nodes; left += 2) { + let right = nodes - 1 - left; + for (let t1 of dp[left]) { + for (let t2 of dp[right]) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ \ No newline at end of file diff --git a/articles/binary-search-tree-iterator.md b/articles/binary-search-tree-iterator.md new file mode 100644 index 000000000..873698c70 --- /dev/null +++ b/articles/binary-search-tree-iterator.md @@ -0,0 +1,699 @@ +## 1. Flattening the BST (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + def dfs(node): + if not node: + return + dfs(node.left) + self.arr.append(node.val) + dfs(node.right) + + dfs(root) + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + dfs(root); + } + + private void dfs(TreeNode node) { + if (node == null) { + return; + } + dfs(node.left); + arr.add(node.val); + dfs(node.right); + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + vector arr; + int itr; + + void dfs(TreeNode* node) { + if (!node) { + return; + } + dfs(node->left); + arr.push_back(node->val); + dfs(node->right); + } + +public: + BSTIterator(TreeNode* root) { + itr = 0; + dfs(root); + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + const dfs = (node) => { + if (!node) { + return; + } + dfs(node.left); + this.arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Flatten the BST (Iterative DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + stack = [] + while root or stack: + while root: + stack.append(root) + root = root.left + root = stack.pop() + self.arr.append(root.val) + root = root.right + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + Stack stack = new Stack<>(); + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + arr.add(root.val); + root = root.right; + } + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + vector arr; + int itr; + +public: + BSTIterator(TreeNode* root) { + itr = 0; + stack stack; + while (root || !stack.empty()) { + while (root) { + stack.push(root); + root = root->left; + } + root = stack.top(); + stack.pop(); + arr.push_back(root->val); + root = root->right; + } + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + let stack = []; + while (root || stack.length) { + while (root) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + this.arr.push(root.val); + root = root.right; + } + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.stack = [] + while root: + self.stack.append(root) + root = root.left + + def next(self) -> int: + res = self.stack.pop() + cur = res.right + while cur: + self.stack.append(cur) + cur = cur.left + return res.val + + def hasNext(self) -> bool: + return bool(self.stack) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private Stack stack; + + public BSTIterator(TreeNode root) { + stack = new Stack<>(); + while (root != null) { + stack.push(root); + root = root.left; + } + } + + public int next() { + TreeNode res = stack.pop(); + TreeNode cur = res.right; + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + return res.val; + } + + public boolean hasNext() { + return !stack.isEmpty(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + stack stack; + +public: + BSTIterator(TreeNode* root) { + while (root) { + stack.push(root); + root = root->left; + } + } + + int next() { + TreeNode* res = stack.top(); + stack.pop(); + TreeNode* cur = res->right; + while (cur) { + stack.push(cur); + cur = cur->left; + } + return res->val; + } + + bool hasNext() { + return !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.stack = []; + while (root) { + this.stack.push(root); + root = root.left; + } + } + + /** + * @return {number} + */ + next() { + let res = this.stack.pop(); + let cur = res.right; + while (cur) { + this.stack.push(cur); + cur = cur.left; + } + return res.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Iterative DFS - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.cur = root + self.stack = [] + + def next(self) -> int: + while self.cur: + self.stack.append(self.cur) + self.cur = self.cur.left + + node = self.stack.pop() + self.cur = node.right + return node.val + + def hasNext(self) -> bool: + return bool(self.cur) or bool(self.stack) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private TreeNode cur; + private Stack stack; + + public BSTIterator(TreeNode root) { + cur = root; + stack = new Stack<>(); + } + + public int next() { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + TreeNode node = stack.pop(); + cur = node.right; + return node.val; + } + + public boolean hasNext() { + return cur != null || !stack.isEmpty(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + TreeNode* cur; + stack stack; + +public: + BSTIterator(TreeNode* root) { + cur = root; + } + + int next() { + while (cur) { + stack.push(cur); + cur = cur->left; + } + + TreeNode* node = stack.top(); + stack.pop(); + cur = node->right; + return node->val; + } + + bool hasNext() { + return cur || !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.cur = root; + this.stack = []; + } + + /** + * @return {number} + */ + next() { + while (this.cur) { + this.stack.push(this.cur); + this.cur = this.cur.left; + } + + let node = this.stack.pop(); + this.cur = node.right; + return node.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.cur !== null || this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. \ No newline at end of file diff --git a/articles/concatenated-words.md b/articles/concatenated-words.md new file mode 100644 index 000000000..0d7eaf0d4 --- /dev/null +++ b/articles/concatenated-words.md @@ -0,0 +1,587 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + n = len(words) + res = [] + wordSet = set(words) + maxLen = 0 + for w in words: + maxLen = max(maxLen, len(w)) + + def dfs(concatWord, totLen): + if len(concatWord) > 1: + word = "".join(concatWord) + if word in wordSet: + res.append(word) + wordSet.remove(word) + + for i in range(len(words)): + if totLen + len(words[i]) > maxLen: + continue + concatWord.append(words[i]) + dfs(concatWord, totLen + len(words[i])) + concatWord.pop() + + dfs([], 0) + return res +``` + +```java +public class Solution { + private Set wordSet; + private int maxLen; + private List res; + private String[] words; + + public List findAllConcatenatedWordsInADict(String[] words) { + this.words = words; + this.wordSet = new HashSet<>(Arrays.asList(words)); + this.maxLen = 0; + this.res = new ArrayList<>(); + + for (String w : words) { + maxLen = Math.max(maxLen, w.length()); + } + + dfs(new ArrayList<>(), 0); + return res; + } + + private void dfs(List concatWord, int totLen) { + if (concatWord.size() > 1) { + String word = String.join("", concatWord); + if (wordSet.contains(word)) { + res.add(word); + wordSet.remove(word); + } + } + + for (String word : words) { + if (totLen + word.length() > maxLen) continue; + concatWord.add(word); + dfs(concatWord, totLen + word.length()); + concatWord.remove(concatWord.size() - 1); + } + } +} +``` + +```cpp +class Solution { +private: + unordered_set wordSet; + int maxLen; + vector res; + vector words; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + this->words = words; + wordSet = unordered_set(words.begin(), words.end()); + maxLen = 0; + res.clear(); + + for (const string& w : words) { + maxLen = max(maxLen, (int)w.length()); + } + + vector concatWord; + dfs(concatWord, 0); + return res; + } + +private: + void dfs(vector& concatWord, int totLen) { + if (concatWord.size() > 1) { + string word = accumulate(concatWord.begin(), concatWord.end(), string("")); + if (wordSet.count(word)) { + res.push_back(word); + wordSet.erase(word); + } + } + + for (const string& word : words) { + if (totLen + word.size() > maxLen) continue; + concatWord.push_back(word); + dfs(concatWord, totLen + word.length()); + concatWord.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + let n = words.length; + let res = []; + let wordSet = new Set(words); + let maxLen = 0; + + for (let w of words) { + maxLen = Math.max(maxLen, w.length); + } + + const dfs = (concatWord, totLen) => { + if (concatWord.length > 1) { + let word = concatWord.join(""); + if (wordSet.has(word)) { + res.push(word); + wordSet.delete(word); + } + } + + for (let i = 0; i < words.length; i++) { + if (totLen + words[i].length > maxLen) continue; + concatWord.push(words[i]); + dfs(concatWord, totLen + words[i].length); + concatWord.pop(); + } + }; + + dfs([], 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + + def dfs(word): + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + return True + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + + const dfs = (word) => { + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + return true; + } + } + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 4)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + dp = {} + + def dfs(word): + if word in dp: + return dp[word] + + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + dp[word] = True + return True + dp[word] = False + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + private Map dp; + + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + dp = new HashMap<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + if (dp.containsKey(word)) { + return dp.get(word); + } + + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + dp.put(word, true); + return true; + } + } + dp.put(word, false); + return false; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + if (dp.count(word)) { + return dp[word]; + } + + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + dp[word] = true; + return true; + } + } + dp[word] = false; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const dp = new Map(); + + const dfs = (word) => { + if (dp.has(word)) { + return dp.get(word); + } + + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + dp.set(word, true); + return true; + } + } + dp.set(word, false); + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + res = [] + + for word in words: + m = len(word) + + dp = [False] * (m + 1) + dp[0] = True + + for i in range(1, m + 1): + for j in range(i): + if j == 0 and i == m: + continue + if dp[j] and word[j:i] in wordSet: + dp[i] = True + break + + if dp[m]: + res.append(word) + + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String word : words) { + int m = word.length(); + boolean[] dp = new boolean[m + 1]; + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.contains(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.add(word); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (string& word : words) { + int m = word.length(); + vector dp(m + 1, false); + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.count(word.substr(j, i - j))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push_back(word); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const res = []; + + for (const word of words) { + const m = word.length; + const dp = new Array(m + 1).fill(false); + dp[0] = true; + + for (let i = 1; i <= m; i++) { + for (let j = 0; j < i; j++) { + if (j === 0 && i === m) continue; + if (dp[j] && wordSet.has(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push(word); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. \ No newline at end of file diff --git a/articles/count-all-valid-pickup-and-delivery-options.md b/articles/count-all-valid-pickup-and-delivery-options.md new file mode 100644 index 000000000..78b58d069 --- /dev/null +++ b/articles/count-all-valid-pickup-and-delivery-options.md @@ -0,0 +1,668 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + + def dfs(picked, delivered): + if picked == n and delivered == n: + return 1 + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countOrders(int n) { + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + + int countOrders(int n) { + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + dp[n][n] = 1 + + def dfs(picked, delivered): + if dp[picked][delivered] != -1: + return dp[picked][delivered] + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + dp[picked][delivered] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int countOrders(int n) { + dp = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i][j] = -1; + } + } + dp[n][n] = 1; + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return dp[picked][delivered] = (int)res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + vector> dp; + + int countOrders(int n) { + dp.assign(n + 1, vector(n + 1, -1)); + dp[n][n] = 1; + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return dp[picked][delivered] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(-1)); + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + if (dp[picked][delivered] !== -1) { + return dp[picked][delivered]; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + dp[picked][delivered] = res; + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[0] * (n + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for picked in range(n + 1): + for delivered in range(n + 1): + if picked < n: + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ) + + if delivered < picked: + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ) + + return dp[n][n] +``` + +```java +public class Solution { + public int countOrders(int n) { + final int MOD = 1_000_000_007; + int[][] dp = new int[n + 1][n + 1]; + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (int) ((dp[picked + 1][delivered] + + (n - picked) * 1L * dp[picked][delivered]) % MOD); + } + if (delivered < picked) { + dp[picked][delivered + 1] = (int) ((dp[picked][delivered + 1] + + (picked - delivered) * 1L * dp[picked][delivered]) % MOD); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1'000'000'007; + vector> dp(n + 1, vector(n + 1, 0)); + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (dp[picked + 1][delivered] + + (n - picked) * 1LL * dp[picked][delivered]) % MOD; + } + if (delivered < picked) { + dp[picked][delivered + 1] = (dp[picked][delivered + 1] + + (picked - delivered) * 1LL * dp[picked][delivered]) % MOD; + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)); + dp[0][0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ); + } + + if (delivered < picked) { + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [0] * (n + 1) + dp[0] = 1 + + for picked in range(n + 1): + for delivered in range(picked): + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ) + + if picked < n: + next_dp = [0] * (n + 1) + for delivered in range(picked + 1): + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ) + dp = next_dp + + return dp[n] +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + int[] dp = new int[n + 1]; + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1L * dp[delivered]) % MOD); + } + + if (picked < n) { + int[] next_dp = new int[n + 1]; + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1L *dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + vector dp(n + 1); + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1LL * dp[delivered]) % MOD); + } + if (picked < n) { + vector next_dp(n + 1); + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1LL * dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ); + } + + if (picked < n) { + let next_dp = new Array(n + 1).fill(0); + for (let delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + slots, res = 2 * n, 1 + while slots > 0: + valid_choices = slots * (slots - 1) // 2 + res = (res * valid_choices) % MOD + slots -= 2 + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long slots = 2 * n, res = 1; + + while (slots > 0) { + long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long slots = 2 * n, res = 1; + + while (slots > 0) { + long long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let slots = 2 * n, res = 1; + + while (slots > 0) { + let validChoices = (slots * (slots - 1)) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Probability + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + res = 1 + + for slot in range(1, 2 * n + 1): + res *= slot + if slot % 2 == 0: + res >>= 1 + res %= MOD + + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = BigInt(1000000007); + let res = BigInt(1); + + for (let slot = 1; slot <= 2 * n; slot++) { + res *= BigInt(slot); + if (slot % 2 === 0) { + res /= BigInt(2); + } + res %= MOD; + } + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/data-stream-as-disjoint-intervals.md b/articles/data-stream-as-disjoint-intervals.md new file mode 100644 index 000000000..a39dc70f3 --- /dev/null +++ b/articles/data-stream-as-disjoint-intervals.md @@ -0,0 +1,450 @@ +## 1. Brute Force (Sorting) + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = [] + + def addNum(self, value: int) -> None: + self.arr.append(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + self.arr.sort() + n = len(self.arr) + start = self.arr[0] + res = [] + for i in range(1, n): + if self.arr[i] - self.arr[i - 1] > 1: + res.append([start, self.arr[i - 1]]) + start = self.arr[i] + + res.append([start, self.arr[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private List arr; + + public SummaryRanges() { + arr = new ArrayList<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + Collections.sort(arr); + int start = arr.get(0); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i) - arr.get(i - 1) > 1) { + res.add(new int[]{start, arr.get(i - 1)}); + start = arr.get(i); + } + } + res.add(new int[]{start, arr.get(arr.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + vector arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.push_back(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + sort(arr.begin(), arr.end()); + int start = arr[0]; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] - arr[i - 1] > 1) { + res.push_back({start, arr[i - 1]}); + start = arr[i]; + } + } + res.push_back({start, arr.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = []; + } + + /** + * @param {number} value + * @return {void} + */ + addNum(value) { + this.arr.push(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.length === 0) return []; + + this.arr.sort((a, b) => a - b); + let start = this.arr[0]; + let res = []; + + for (let i = 1; i < this.arr.length; i++) { + if (this.arr[i] - this.arr[i - 1] > 1) { + res.push([start, this.arr[i - 1]]); + start = this.arr[i]; + } + } + res.push([start, this.arr[this.arr.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Set + Sorting + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = set() + + def addNum(self, value: int) -> None: + self.arr.add(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + lst = sorted(list(self.arr)) + n = len(lst) + start = lst[0] + res = [] + for i in range(1, n): + if lst[i] - lst[i - 1] > 1: + res.append([start, lst[i - 1]]) + start = lst[i] + + res.append([start, lst[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private Set arr; + + public SummaryRanges() { + arr = new TreeSet<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + List lst = new ArrayList<>(arr); + int start = lst.get(0); + for (int i = 1; i < lst.size(); i++) { + if (lst.get(i) - lst.get(i - 1) > 1) { + res.add(new int[]{start, lst.get(i - 1)}); + start = lst.get(i); + } + } + res.add(new int[]{start, lst.get(lst.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.insert(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + vector lst(arr.begin(), arr.end()); + int start = lst[0]; + + for (int i = 1; i < lst.size(); i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push_back({start, lst[i - 1]}); + start = lst[i]; + } + } + res.push_back({start, lst.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = new Set(); + } + + /** + * @param {number} value + * @return {number[][]} + */ + addNum(value) { + this.arr.add(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.size === 0) return []; + + let lst = Array.from(this.arr).sort((a, b) => a - b); + let start = lst[0]; + let res = []; + + for (let i = 1; i < lst.length; i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push([start, lst[i - 1]]); + start = lst[i]; + } + } + res.push([start, lst[lst.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Ordered Map + +::tabs-start + +```python +from sortedcontainers import SortedDict + +class SummaryRanges: + def __init__(self): + self.treeMap = SortedDict() + + def addNum(self, value: int) -> None: + self.treeMap[value] = True + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.treeMap: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeMap treeMap; + + public SummaryRanges() { + treeMap = new TreeMap<>(); + } + + public void addNum(int value) { + treeMap.put(value, true); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : treeMap.keySet()) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + map treeMap; + +public: + SummaryRanges() {} + + void addNum(int value) { + treeMap[value] = true; + } + + vector> getIntervals() { + vector> res; + for (auto& [n, _] : treeMap) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Ordered Set + +::tabs-start + +```python +from sortedcontainers import SortedSet + +class SummaryRanges: + def __init__(self): + self.orderedSet = SortedSet() + + def addNum(self, value: int) -> None: + self.orderedSet.add(value) + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.orderedSet: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeSet orderedSet; + + public SummaryRanges() { + orderedSet = new TreeSet<>(); + } + + public void addNum(int value) { + orderedSet.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : orderedSet) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set orderedSet; + +public: + SummaryRanges() {} + + void addNum(int value) { + orderedSet.insert(value); + } + + vector> getIntervals() { + vector> res; + for (int n : orderedSet) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-browser-history.md b/articles/design-browser-history.md new file mode 100644 index 000000000..a557f9151 --- /dev/null +++ b/articles/design-browser-history.md @@ -0,0 +1,640 @@ +## 1. Two Stacks + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.back_history = [homepage] + self.front_history = [] + + def visit(self, url: str) -> None: + self.back_history.append(url) + self.front_history = [] + + def back(self, steps: int) -> str: + while steps and len(self.back_history) > 1: + self.front_history.append(self.back_history.pop()) + steps -= 1 + return self.back_history[-1] + + def forward(self, steps: int) -> str: + while steps and self.front_history: + self.back_history.append(self.front_history.pop()) + steps -= 1 + return self.back_history[-1] +``` + +```java +public class BrowserHistory { + private Stack backHistory; + private Stack frontHistory; + + public BrowserHistory(String homepage) { + backHistory = new Stack<>(); + frontHistory = new Stack<>(); + backHistory.push(homepage); + } + + public void visit(String url) { + backHistory.push(url); + frontHistory = new Stack<>(); + } + + public String back(int steps) { + while (steps > 0 && backHistory.size() > 1) { + frontHistory.push(backHistory.pop()); + steps--; + } + return backHistory.peek(); + } + + public String forward(int steps) { + while (steps > 0 && !frontHistory.isEmpty()) { + backHistory.push(frontHistory.pop()); + steps--; + } + return backHistory.peek(); + } +} +``` + +```cpp +class BrowserHistory { +private: + stack backHistory, frontHistory; + +public: + BrowserHistory(string homepage) { + backHistory.push(homepage); + } + + void visit(string url) { + backHistory.push(url); + frontHistory = stack(); + } + + string back(int steps) { + while (steps-- && backHistory.size() > 1) { + frontHistory.push(backHistory.top()); + backHistory.pop(); + } + return backHistory.top(); + } + + string forward(int steps) { + while (steps-- && !frontHistory.empty()) { + backHistory.push(frontHistory.top()); + frontHistory.pop(); + } + return backHistory.top(); + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.backHistory = [homepage]; + this.frontHistory = []; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.backHistory.push(url); + this.frontHistory = []; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (steps-- > 0 && this.backHistory.length > 1) { + this.frontHistory.push(this.backHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (steps-- > 0 && this.frontHistory.length > 0) { + this.backHistory.push(this.frontHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. + +--- + +## 2. Dynamic Array + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + + def visit(self, url: str) -> None: + self.cur += 1 + self.history = self.history[:self.cur] + self.history.append(url) + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(len(self.history) - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + } + + public void visit(String url) { + cur++; + history = history.subList(0, cur); + history.add(url); + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(history.size() - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + } + + void visit(string url) { + cur++; + history.resize(cur); + history.push_back(url); + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min((int)history.size() - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + this.history = this.history.slice(0, this.cur); + this.history.push(url); + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.history.length - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 3. Dynamic Array (Optimal) + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + self.n = 1 + + def visit(self, url: str) -> None: + self.cur += 1 + if self.cur == len(self.history): + self.history.append(url) + self.n += 1 + else: + self.history[self.cur] = url + self.n = self.cur + 1 + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(self.n - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + private int n; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + n = 1; + } + + public void visit(String url) { + cur++; + if (cur == history.size()) { + history.add(url); + n++; + } else { + history.set(cur, url); + n = cur + 1; + } + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(n - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur, n; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + n = 1; + } + + void visit(string url) { + cur++; + if (cur == history.size()) { + history.push_back(url); + n++; + } else { + history[cur] = url; + n = cur + 1; + } + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min(n - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + this.n = 1; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + if (this.cur === this.history.length) { + this.history.push(url); + this.n++; + } else { + this.history[this.cur] = url; + this.n = this.cur + 1; + } + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.n - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 4. Doubly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class BrowserHistory: + + def __init__(self, homepage: str): + self.cur = ListNode(homepage) + + def visit(self, url: str) -> None: + self.cur.next = ListNode(url, self.cur) + self.cur = self.cur.next + + def back(self, steps: int) -> str: + while self.cur.prev and steps > 0: + self.cur = self.cur.prev + steps -= 1 + return self.cur.val + + def forward(self, steps: int) -> str: + while self.cur.next and steps > 0: + self.cur = self.cur.next + steps -= 1 + return self.cur.val +``` + +```java +class ListNode { + String val; + ListNode prev, next; + + public ListNode(String val, ListNode prev, ListNode next) { + this.val = val; + this.prev = prev; + this.next = next; + } + + public ListNode(String val) { + this(val, null, null); + } +} + +public class BrowserHistory { + private ListNode cur; + + public BrowserHistory(String homepage) { + cur = new ListNode(homepage); + } + + public void visit(String url) { + cur.next = new ListNode(url, cur, null); + cur = cur.next; + } + + public String back(int steps) { + while (cur.prev != null && steps > 0) { + cur = cur.prev; + steps--; + } + return cur.val; + } + + public String forward(int steps) { + while (cur.next != null && steps > 0) { + cur = cur.next; + steps--; + } + return cur.val; + } +} +``` + +```cpp +class BrowserHistory { + struct ListNode { + public: + string val; + ListNode* prev; + ListNode* next; + + ListNode(string val, ListNode* prev = nullptr, ListNode* next = nullptr) + : val(val), prev(prev), next(next) {} + }; + + ListNode* cur; + +public: + BrowserHistory(string homepage) { + cur = new ListNode(homepage); + } + + void visit(string url) { + cur->next = new ListNode(url, cur, nullptr); + cur = cur->next; + } + + string back(int steps) { + while (cur->prev != nullptr && steps > 0) { + cur = cur->prev; + steps--; + } + return cur->val; + } + + string forward(int steps) { + while (cur->next != nullptr && steps > 0) { + cur = cur->next; + steps--; + } + return cur->val; + } +}; +``` + +```javascript +class ListNode { + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.cur = new ListNode(homepage); + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur.next = new ListNode(url, this.cur, null); + this.cur = this.cur.next; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (this.cur.prev !== null && steps > 0) { + this.cur = this.cur.prev; + steps--; + } + return this.cur.val; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (this.cur.next !== null && steps > 0) { + this.cur = this.cur.next; + steps--; + } + return this.cur.val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. \ No newline at end of file diff --git a/articles/detonate-the-maximum-bombs.md b/articles/detonate-the-maximum-bombs.md new file mode 100644 index 000000000..bc1a75fc9 --- /dev/null +++ b/articles/detonate-the-maximum-bombs.md @@ -0,0 +1,522 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + adj = [[] for _ in range(len(bombs))] + + for i in range(len(bombs)): + x1, y1, r1 = bombs[i] + for j in range(i + 1, len(bombs)): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + def dfs(i, visit): + if i in visit: + return 0 + visit.add(i) + for nei in adj[i]: + dfs(nei, visit) + return len(visit) + + res = 0 + for i in range(len(bombs)): + res = max(res, dfs(i, set())) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) { + adj[i].add(j); + } + if (d <= (long) r2 * r2) { + adj[j].add(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new HashSet<>(), adj)); + } + return res; + } + + private int dfs(int i, Set visit, List[] adj) { + if (!visit.add(i)) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + long long x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + long long x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + if (d <= r1 * r1) { + adj[i].push_back(j); + } + if (d <= r2 * r2) { + adj[j].push_back(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + unordered_set visit; + res = max(res, dfs(i, visit, adj)); + } + return res; + } + +private: + int dfs(int i, unordered_set& visit, vector>& adj) { + if (!visit.insert(i).second) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let [x1, y1, r1] = bombs[i]; + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + const dfs = (i, visit) => { + if (visit.has(i)) return 0; + visit.add(i); + for (let nei of adj[i]) { + dfs(nei, visit); + } + return visit.size; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new Set())); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + q = deque([i]) + visit = [False] * n + visit[i] = True + count = 1 + while q: + node = q.popleft() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + q.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Queue q = new LinkedList<>(); + boolean[] visit = new boolean[n]; + q.offer(i); + visit[i] = true; + int count = 1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.offer(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * 1LL * (x1 - x2) + (y1 - y2) * 1LL * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + queue q; + vector visit(n, false); + q.push(i); + visit[i] = true; + int count = 1; + + while (!q.empty()) { + int node = q.front();q.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let q = new Queue([i]); + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + stack = [i] + visit = [False] * n + visit[i] = True + count = 1 + + while stack: + node = stack.pop() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + stack.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Stack stack = new Stack<>(); + boolean[] visit = new boolean[n]; + stack.push(i); + visit[i] = true; + int count = 1; + + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (long long)(x1 - x2) * (x1 - x2) + (long long)(y1 - y2) * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + stack stk; + vector visit(n, false); + stk.push(i); + visit[i] = true; + int count = 1; + + while (!stk.empty()) { + int node = stk.top();stk.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stk.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let stack = [i]; + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (stack.length) { + let node = stack.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/distribute-coins-in-binary-tree.md b/articles/distribute-coins-in-binary-tree.md new file mode 100644 index 000000000..4e26d25e9 --- /dev/null +++ b/articles/distribute-coins-in-binary-tree.md @@ -0,0 +1,723 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return [0, 0] # [size, coins] + + l_size, l_coins = dfs(cur.left) + r_size, r_coins = dfs(cur.right) + + size = 1 + l_size + r_size + coins = cur.val + l_coins + r_coins + self.res += abs(size - coins) + + return [size, coins] + + dfs(root) + return self.res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int[] dfs(TreeNode cur) { + if (cur == null) { + return new int[]{0, 0}; // [size, coins] + } + + int[] left = dfs(cur.left); + int[] right = dfs(cur.right); + + int size = 1 + left[0] + right[0]; + int coins = cur.val + left[1] + right[1]; + res += Math.abs(size - coins); + + return new int[]{size, coins}; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + int res; + + vector dfs(TreeNode* cur) { + if (!cur) { + return {0, 0}; // [size, coins] + } + + vector left = dfs(cur->left); + vector right = dfs(cur->right); + + int size = 1 + left[0] + right[0]; + int coins = cur->val + left[1] + right[1]; + res += abs(size - coins); + + return {size, coins}; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return [0, 0]; // [size, coins] + } + + let [lSize, lCoins] = dfs(cur.left); + let [rSize, rCoins] = dfs(cur.right); + + let size = 1 + lSize + rSize; + let coins = cur.val + lCoins + rCoins; + res += Math.abs(size - coins); + + return [size, coins]; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return 0 # extra_coins + + l_extra = dfs(cur.left) + r_extra = dfs(cur.right) + + extra_coins = cur.val - 1 + l_extra + r_extra + self.res += abs(extra_coins) + return extra_coins + + dfs(root) + return self.res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int dfs(TreeNode cur) { + if (cur == null) { + return 0; // extra_coins + } + + int lExtra = dfs(cur.left); + int rExtra = dfs(cur.right); + + int extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + int res; + + int dfs(TreeNode* cur) { + if (!cur) { + return 0; // extra_coins + } + + int lExtra = dfs(cur->left); + int rExtra = dfs(cur->right); + + int extraCoins = cur->val - 1 + lExtra + rExtra; + res += abs(extraCoins); + return extraCoins; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return 0; // extra_coins + } + + let lExtra = dfs(cur.left); + let rExtra = dfs(cur.right); + + let extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ fo recursion stack. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([root]) + parent_map = {} + + nodes = [] + while q: + node = q.popleft() + nodes.append(node) + if node.left: + parent_map[node.left] = node + q.append(node.left) + if node.right: + parent_map[node.right] = node + q.append(node.right) + + while nodes: + node = nodes.pop() + if node in parent_map: + parent = parent_map[node] + parent.val += node.val - 1 + res += abs(node.val - 1) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + int res = 0; + Queue q = new LinkedList<>(); + Map parentMap = new HashMap<>(); + List nodes = new ArrayList<>(); + + q.offer(root); + while (!q.isEmpty()) { + TreeNode node = q.poll(); + nodes.add(node); + if (node.left != null) { + parentMap.put(node.left, node); + q.offer(node.left); + } + if (node.right != null) { + parentMap.put(node.right, node); + q.offer(node.right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode node = nodes.get(i); + if (parentMap.containsKey(node)) { + TreeNode parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int distributeCoins(TreeNode* root) { + int res = 0; + queue q; + unordered_map parentMap; + vector nodes; + + q.push(root); + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + nodes.push_back(node); + if (node->left) { + parentMap[node->left] = node; + q.push(node->left); + } + if (node->right) { + parentMap[node->right] = node; + q.push(node->right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode* node = nodes[i]; + if (parentMap.count(node)) { + TreeNode* parent = parentMap[node]; + parent->val += node->val - 1; + res += abs(node->val - 1); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + let q = new Queue(); + let parentMap = new Map(); + let nodes = []; + + q.push(root); + while (!q.isEmpty()) { + let node = q.pop(); + nodes.push(node); + if (node.left) { + parentMap.set(node.left, node); + q.push(node.left); + } + if (node.right) { + parentMap.set(node.right, node); + q.push(node.right); + } + } + + for (let i = nodes.length - 1; i >= 0; i--) { + let node = nodes[i]; + if (parentMap.has(node)) { + let parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + stack = [root] + res = 0 + visit = set() + + while stack: + node = stack.pop() + + if node not in visit: + stack.append(node) + visit.add(node) + + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + else: + if node.left: + node.val += node.left.val + if node.right: + node.val += node.right.val + + node.val -= 1 + res += abs(node.val) + visit.remove(node) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + Stack stack = new Stack<>(); + Set visit = new HashSet<>(); + stack.push(root); + int res = 0; + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (!visit.contains(node)) { + stack.push(node); + visit.add(node); + + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } else { + if (node.left != null) { + node.val += node.left.val; + } + if (node.right != null) { + node.val += node.right.val; + } + + node.val -= 1; + res += Math.abs(node.val); + visit.remove(node); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int distributeCoins(TreeNode* root) { + stack stack; + unordered_set visit; + stack.push(root); + int res = 0; + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + + if (visit.find(node) == visit.end()) { + stack.push(node); + visit.insert(node); + + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + } else { + if (node->left) { + node->val += node->left->val; + } + if (node->right) { + node->val += node->right->val; + } + + visit.erase(node); + node->val -= 1; + res += abs(node->val); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let stack = [root]; + let visit = new Set(); + let res = 0; + + while (stack.length) { + let node = stack.pop(); + + if (!visit.has(node)) { + stack.push(node); + visit.add(node); + + if (node.right) { + stack.push(node.right); + } + if (node.left) { + stack.push(node.left); + } + } else { + if (node.left) { + node.val += node.left.val; + } + if (node.right) { + node.val += node.right.val; + } + + visit.delete(node); + node.val -= 1; + res += Math.abs(node.val); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/eliminate-maximum-number-of-monsters.md b/articles/eliminate-maximum-number-of-monsters.md new file mode 100644 index 000000000..551c82473 --- /dev/null +++ b/articles/eliminate-maximum-number-of-monsters.md @@ -0,0 +1,298 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minReach = [math.ceil(d / s) for d, s in zip(dist, speed)] + minReach.sort() + + res = 0 + for minute in range(len(minReach)): + if minute >= minReach[minute]: + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + int[] minReach = new int[n]; + + for (int i = 0; i < n; i++) { + minReach[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(minReach); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + vector minReach(n); + + for (int i = 0; i < n; i++) { + minReach[i] = ceil((double)dist[i] / speed[i]); + } + + sort(minReach.begin(), minReach.end()); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + let minReach = new Array(n); + + for (let i = 0; i < n; i++) { + minReach[i] = Math.ceil(dist[i] / speed[i]); + } + + minReach.sort((a, b) => a - b); + + let res = 0; + for (let minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting (Overwrting Input Array) + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + for i in range(len(dist)): + dist[i] = math.ceil(dist[i] / speed[i]) + + dist.sort() + for minute in range(len(dist)): + if minute >= dist[minute]: + return minute + + return len(dist) +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + for (int i = 0; i < n; i++) { + dist[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(dist); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + for (int i = 0; i < n; i++) { + dist[i] = ceil((double)dist[i] / speed[i]); + } + + sort(dist.begin(), dist.end()); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + for (let i = 0; i < n; i++) { + dist[i] = Math.ceil(dist[i] / speed[i]); + } + + dist.sort((a, b) => a - b); + for (let minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minHeap = [] + for i in range(len(dist)): + heapq.heappush(minHeap, dist[i] / speed[i]) + + res = 0 + while minHeap: + if res >= heapq.heappop(minHeap): + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + PriorityQueue minHeap = new PriorityQueue<>(); + for (int i = 0; i < dist.length; i++) { + minHeap.add((double) dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.poll()) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + priority_queue, greater> minHeap; + for (int i = 0; i < dist.size(); i++) { + minHeap.push((double)dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.empty()) { + if (res >= minHeap.top()) { + return res; + } + minHeap.pop(); + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + const minHeap = new MinPriorityQueue(); + for (let i = 0; i < dist.length; i++) { + minHeap.enqueue(dist[i] / speed[i]); + } + + let res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.dequeue().element) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-all-people-with-secret.md b/articles/find-all-people-with-secret.md new file mode 100644 index 000000000..1d1b58e20 --- /dev/null +++ b/articles/find-all-people-with-secret.md @@ -0,0 +1,834 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + def dfs(src, adj): + if src in visit: + return + visit.add(src) + secrets.add(src) + for nei in adj[src]: + dfs(nei, adj) + + for t in sorted(time_map.keys()): + visit = set() + for src in time_map[t]: + if src in secrets: + dfs(src, time_map[t]) + + return list(secrets) +``` + +```java +public class Solution { + private Set secrets, visit; + + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + secrets = new HashSet<>(); + visit = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + Map>> time_map = new HashMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + List timeKeys = new ArrayList<>(time_map.keySet()); + Collections.sort(timeKeys); + for (int t : timeKeys) { + visit = new HashSet<>(); + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + dfs(src, time_map.get(t)); + } + } + } + return new ArrayList<>(secrets); + } + + private void dfs(int src, Map> adj) { + if (!visit.add(src)) return; + secrets.add(src); + for (int nei : adj.getOrDefault(src, new ArrayList<>())) { + dfs(nei, adj); + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set secrets, visit; + + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + secrets = {0, firstPerson}; + unordered_map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + vector timeKeys; + for (auto& [t, _] : time_map) { + timeKeys.push_back(t); + } + sort(timeKeys.begin(), timeKeys.end()); + + for (int& t : timeKeys) { + visit.clear(); + for (auto& [src, _] : time_map[t]) { + if (secrets.count(src)) { + dfs(src, time_map[t]); + } + } + } + + return vector(secrets.begin(), secrets.end()); + } + +private: + void dfs(int src, unordered_map>& adj) { + if (!visit.insert(src).second) return; + secrets.insert(src); + for (int& nei : adj[src]) { + dfs(nei, adj); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + let visit = new Set(); + let time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + const dfs = (src, adj) => { + if (visit.has(src)) return; + visit.add(src); + secrets.add(src); + for (let nei of (adj.get(src) || [])) { + dfs(nei, adj); + } + }; + + let timeKeys = [...time_map.keys()].sort((a, b) => a - b); + for (let t of timeKeys) { + visit.clear(); + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + dfs(src, time_map.get(t)); + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + for t in sorted(time_map.keys()): + visit = set() + q = deque() + + for src in time_map[t]: + if src in secrets: + q.append(src) + visit.add(src) + + while q: + node = q.popleft() + secrets.add(node) + for nei in time_map[t][node]: + if nei not in visit: + visit.add(nei) + q.append(nei) + + return list(secrets) +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Set secrets = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + TreeMap>> time_map = new TreeMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + for (int t : time_map.keySet()) { + Set visit = new HashSet<>(); + Queue q = new LinkedList<>(); + + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + q.offer(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + secrets.add(node); + for (int nei : time_map.get(t).get(node)) { + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + + return new ArrayList<>(secrets); + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + unordered_set secrets = {0, firstPerson}; + map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + for (auto& [t, adj] : time_map) { + unordered_set visit; + queue q; + + for (auto& [src, _] : adj) { + if (secrets.count(src)) { + q.push(src); + visit.insert(src); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + secrets.insert(node); + for (int nei : adj[node]) { + if (!visit.count(nei)) { + visit.insert(nei); + q.push(nei); + } + } + } + } + + return vector(secrets.begin(), secrets.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + const time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + + for (let t of [...time_map.keys()].sort((a, b) => a - b)) { + let visit = new Set(); + const q = new Queue(); + + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + q.push(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + secrets.add(node); + for (let nei of time_map.get(t).get(node)) { + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + secrets = [False] * n + secrets[0] = secrets[firstPerson] = True + + for _, group in groupby(meetings, key=lambda x: x[2]): + adj = defaultdict(list) + visited = set() + + for u, v, _ in group: + adj[u].append(v) + adj[v].append(u) + if secrets[u]: + visited.add(u) + if secrets[v]: + visited.add(v) + + stack = list(visited) + while stack: + node = stack.pop() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + stack.append(nei) + secrets[nei] = True + + return [i for i in range(n) if secrets[i]] +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + boolean[] secrets = new boolean[n]; + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.length; + while (i < m) { + int time = meetings[i][2]; + Map> adj = new HashMap<>(); + Set visited = new HashSet<>(); + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj.computeIfAbsent(u, k -> new ArrayList<>()).add(v); + adj.computeIfAbsent(v, k -> new ArrayList<>()).add(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + Stack stack = new Stack<>(); + stack.addAll(visited); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj.getOrDefault(node, Collections.emptyList())) { + if (!visited.contains(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + List res = new ArrayList<>(); + for (int j = 0; j < n; j++) { + if (secrets[j]) res.add(j); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto& a, auto& b) { + return a[2] < b[2]; + }); + + vector secrets(n, false); + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.size(); + while (i < m) { + int time = meetings[i][2]; + unordered_map> adj; + unordered_set visited; + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj[u].push_back(v); + adj[v].push_back(u); + if (secrets[u]) visited.insert(u); + if (secrets[v]) visited.insert(v); + i++; + } + + stack stack(visited.begin(), visited.end()); + while (!stack.empty()) { + int node = stack.top(); stack.pop(); + for (int& nei : adj[node]) { + if (!visited.count(nei)) { + visited.insert(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + vector res; + for (int j = 0; j < n; j++) { + if (secrets[j]) res.push_back(j); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + const secrets = new Array(n).fill(false); + secrets[0] = secrets[firstPerson] = true; + + let i = 0, m = meetings.length; + while (i < m) { + const time = meetings[i][2]; + const adj = new Map(); + const visited = new Set(); + + while (i < m && meetings[i][2] === time) { + const [u, v] = meetings[i]; + if (!adj.has(u)) adj.set(u, []); + if (!adj.has(v)) adj.set(v, []); + adj.get(u).push(v); + adj.get(v).push(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + const stack = [...visited]; + while (stack.length) { + const node = stack.pop(); + for (const nei of (adj.get(node) || [])) { + if (!visited.has(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + let res = []; + for (let i = 0; i < n; i++) { + if (secrets[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def reset(self, node): + self.Parent[node] = node + self.Size[node] = 1 + +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + dsu = DSU(n) + dsu.union(0, firstPerson) + + for _, group in groupby(meetings, key=lambda x: x[2]): + group_nodes = set() + for u, v, _ in group: + dsu.union(u, v) + group_nodes.add(u) + group_nodes.add(v) + + for node in group_nodes: + if dsu.find(node) != dsu.find(0): + dsu.reset(node) + + return [i for i in range(n) if dsu.find(i) == dsu.find(0)] +``` + +```java +class DSU { + int[] Parent, Size; + + DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +} + +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + DSU dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (int i = 0; i < meetings.length; ) { + int time = meetings[i][2]; + Set group = new HashSet<>(); + + for (; i < meetings.length && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.add(i); + } + return result; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +}; + +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto &a, auto &b) { + return a[2] < b[2]; + }); + DSU dsu(n); + dsu.unionSets(0, firstPerson); + + for (int i = 0; i < meetings.size(); ) { + int time = meetings[i][2]; + unordered_set group; + + for (; i < meetings.size() && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.unionSets(u, v); + group.insert(u); + group.insert(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } + + /** + * @param {number} node + * @return {void} + */ + reset(node) { + this.Parent[node] = node; + this.Size[node] = 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + let dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (let i = 0; i < meetings.length; ) { + let time = meetings[i][2]; + let group = new Set(); + + for (; i < meetings.length && meetings[i][2] === time; i++) { + let [u, v] = meetings[i]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + group.forEach(node => { + if (dsu.find(node) !== dsu.find(0)) { + dsu.reset(node); + } + }); + } + + return [...Array(n).keys()].filter(i => dsu.find(i) === dsu.find(0)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + (m * α(n)))$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space depending on the sorting algorithm. + +> Where $m$ is the number of meetings and $n$ is the number of people. \ No newline at end of file diff --git a/articles/find-the-maximum-sum-of-node-values.md b/articles/find-the-maximum-sum-of-node-values.md new file mode 100644 index 000000000..d48258892 --- /dev/null +++ b/articles/find-the-maximum-sum-of-node-values.md @@ -0,0 +1,637 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + adj = [[] for _ in range(len(nums))] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, par): + res = [nums[node], nums[node] ^ k] + for child in adj[node]: + if child == par: + continue + + cur = dfs(child, node) + tmp = [] + tmp.append(max(res[0] + cur[0], res[1] + cur[1])) + tmp.append(max(res[1] + cur[0], res[0] + cur[1])) + res = tmp + + return res + + return dfs(0, -1)[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, nums, k, adj)[0]; + } + + private long[] dfs(int node, int parent, int[] nums, int k, List[] adj) { + long[] res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + long[] cur = dfs(child, node, nums, k, adj); + long[] tmp = new long[2]; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +} +``` + +```cpp +class Solution { + vector> adj; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, nums, k)[0]; + } + +private: + vector dfs(int node, int parent, vector& nums, int k) { + vector res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + vector cur = dfs(child, node, nums, k); + vector tmp(2); + tmp[0] = max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let res = [nums[node], nums[node] ^ k]; + + for (const child of adj[node]) { + if (child === parent) continue; + + const cur = dfs(child, node); + const tmp = []; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + + return res; + }; + + return dfs(0, -1)[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [[None] * 2 for _ in range(len(nums))] + [[0, float("-inf")]] + + def dfs(i, xorCnt): + if dp[i][xorCnt] is not None: + return dp[i][xorCnt] + + res = nums[i] + dfs(i + 1, xorCnt) + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)) + dp[i][xorCnt] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private long[][] dp; + + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + dp = new long[n + 1][2]; + for (long[] row : dp) Arrays.fill(row, Long.MIN_VALUE); + dp[n][0] = 0; + dp[n][1] = Integer.MIN_VALUE; + + return dfs(0, 0, nums, k); + } + + private long dfs(int i, int xorCnt, int[] nums, int k) { + if (dp[i][xorCnt] != Long.MIN_VALUE) { + return dp[i][xorCnt]; + } + + long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + + return dp[i][xorCnt] = res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + dp.assign(n + 1, vector(2, LLONG_MIN)); + dp[n][0] = 0; + dp[n][1] = INT_MIN; + + return dfs(0, 0, nums, k); + } + +private: + long long dfs(int i, int xorCnt, vector& nums, int k) { + if (dp[i][xorCnt] != LLONG_MIN) { + return dp[i][xorCnt]; + } + + long long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + return dp[i][xorCnt] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [null, null]); + dp[n][0] = 0; + dp[n][1] = -Infinity; + + const dfs = (i, xorCnt) => { + if (dp[i][xorCnt] !== null) return dp[i][xorCnt]; + + let res = nums[i] + dfs(i + 1, xorCnt); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)); + return dp[i][xorCnt] = res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + n = len(nums) + dp = [[0, 0] for _ in range(n + 1)] + dp[n][1] = float("-inf") + + for i in range(n - 1, -1, -1): + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]) + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]) + + return dp[0][0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[][] dp = new long[n + 1][2]; + dp[n][1] = Integer.MIN_VALUE; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector> dp(n + 1, vector(2)); + dp[n][1] = INT_MIN; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + dp[n][1] = -Infinity; + + for (let i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [0, float("-inf")] + + for i in range(len(nums) - 1, -1, -1): + next_dp = [0, 0] + next_dp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]) + next_dp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[] dp = {0, Long.MIN_VALUE}; + + for (int i = n - 1; i >= 0; i--) { + long[] nextDp = new long[2]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector dp = {0, LLONG_MIN}; + + for (int i = n - 1; i >= 0; i--) { + vector nextDp(2); + nextDp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let dp = [0, -Infinity]; + + for (let i = n - 1; i >= 0; i--) { + let nextDp = [0, 0]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + delta = [(num ^ k) - num for num in nums] + delta.sort(reverse=True) + res = sum(nums) + + for i in range(0, len(nums), 2): + if i == len(nums) - 1: + break + path_delta = delta[i] + delta[i + 1] + if path_delta <= 0: + break + res += path_delta + + return res +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + int[] delta = new int[n]; + long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + Arrays.sort(delta); + for (int i = n - 1; i > 0; i -= 2) { + int pathDelta = delta[i] + delta[i - 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector delta(n); + long long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + sort(delta.rbegin(), delta.rend()); + + for (int i = 0; i + 1 < n; i += 2) { + int pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let res = 0; + let delta = []; + for (let i = 0; i < n; i++) { + res += nums[i]; + delta.push((nums[i] ^ k) - nums[i]); + } + + delta.sort((a, b) => b - a); + for (let i = 0; i + 1 < n; i += 2) { + let pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + xorCnt = res = 0 + minDiff = 1 << 30 + + for num in nums: + xorNum = num ^ k + if xorNum > num: + res += xorNum + xorCnt ^= 1 + else: + res += num + minDiff = min(minDiff, abs(xorNum - num)) + + return res - xorCnt * minDiff +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int xorCnt = 0, minDiff = 1 << 30; + long res = 0; + + for (int num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - xorCnt * minDiff; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int xorCnt = 0, minDiff = 1 << 30; + long long res = 0; + + for (int& num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = min(minDiff, abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + let xorCnt = 0, res = 0, minDiff = 1 << 30; + + for (let num of nums) { + let xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-the-safest-path-in-a-grid.md b/articles/find-the-safest-path-in-a-grid.md new file mode 100644 index 000000000..f547fa423 --- /dev/null +++ b/articles/find-the-safest-path-in-a-grid.md @@ -0,0 +1,1054 @@ +## 1. Multi Source BFS + Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid: List[List[int]]) -> int: + N = len(grid) + + def in_bounds(r, c): + return min(r, c) >= 0 and max(r, c) < N + + def precompute(): + q = deque() + min_dist = {} + for r in range(N): + for c in range(N): + if grid[r][c]: + q.append([r, c, 0]) + min_dist[(r, c)] = 0 + while q: + r, c, dist = q.popleft() + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in min_dist: + min_dist[(r2, c2)] = dist + 1 + q.append([r2, c2, dist + 1]) + return min_dist + + min_dist = precompute() + maxHeap = [(-min_dist[(0, 0)], 0, 0)] # (dist, r, c) + visit = set() + visit.add((0, 0)) + + while maxHeap: + dist, r, c = heapq.heappop(maxHeap) + dist = -dist + if (r, c) == (N-1, N-1): + return dist + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in visit: + visit.add((r2, c2)) + dist2 = min(dist, min_dist[(r2, c2)]) + heapq.heappush(maxHeap, (-dist2, r2, c2)) +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = precompute(grid, N); + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + boolean[][] visit = new boolean[N][N]; + + maxHeap.offer(new int[]{minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + int[] curr = maxHeap.poll(); + int dist = curr[0], r = curr[1], c = curr[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.offer(new int[]{dist2, r2, c2}); + } + } + } + return 0; + } + + private int[][] precompute(List> grid, int N) { + int[][] minDist = new int[N][N]; + for (int[] row : minDist) Arrays.fill(row, -1); + Queue q = new LinkedList<>(); + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(new int[]{r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int r = curr[0], c = curr[1], dist = curr[2]; + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.offer(new int[]{r2, c2, dist + 1}); + } + } + } + return minDist; + } + + private boolean inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector> minDist = precompute(grid, N); + priority_queue> maxHeap; + vector> visit(N, vector(N, false)); + + maxHeap.push({minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.empty()) { + vector cur = maxHeap.top(); maxHeap.pop(); + int dist = cur[0], r = cur[1], c = cur[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = min(dist, minDist[r2][c2]); + maxHeap.push({dist2, r2, c2}); + } + } + } + return 0; + } + +private: + vector> precompute(vector>& grid, int N) { + vector> minDist(N, vector(N, -1)); + queue> q; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push({r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.empty()) { + vector cur = q.front(); + q.pop(); + int r = cur[0], c = cur[1], dist = cur[2]; + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.push({r2, c2, dist + 1}); + } + } + } + return minDist; + } + + bool inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const inBounds = (r, c) => { + return r >= 0 && c >= 0 && r < N && c < N; + }; + + const precompute = () => { + const q = new Queue(); + const minDist = Array.from({ length: N }, () => Array(N).fill(-1)); + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push([r, c, 0]); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + let [r, c, dist] = q.pop(); + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && minDist[r2][c2] === -1) { + minDist[r2][c2] = dist + 1; + q.push([r2, c2, dist + 1]); + } + } + } + return minDist; + }; + + const minDist = precompute(); + const maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + + maxHeap.enqueue([minDist[0][0], 0, 0]); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + let [dist, r, c] = maxHeap.dequeue().element; + + if (r === N - 1 && c === N - 1) { + return dist; + } + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && !visit[r2][c2]) { + visit[r2][c2] = true; + let dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.enqueue([dist2, r2, c2]); + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Multi Source BFS + Dijkstra's Algorithm (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + maxHeap = [(-minDist[0][0], 0)] + safeFactor = [0] * (N * N) + safeFactor[0] = minDist[0][0] + + while maxHeap: + dist, node = heapq.heappop(maxHeap) + dist = -dist + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return dist + if safeFactor[node] > dist: + continue + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N: + dist2 = min(dist, minDist[r2][c2]) + if dist2 > safeFactor[node2]: + safeFactor[node2] = dist2 + heapq.heappush(maxHeap, (-dist2, node2)) + + return 0 +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + int[] directions = {0, 1, 0, -1, 0}; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b[0], a[0])); + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + safeFactor[0] = minDist[0][0]; + maxHeap.offer(new int[]{safeFactor[0], 0}); + + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + int dist = top[0], node = top[1]; + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.offer(new int[]{dist2, node2}); + } + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector directions = {0, 1, 0, -1, 0}; + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + priority_queue> maxHeap; + vector safeFactor(N * N, 0); + safeFactor[0] = minDist[0][0]; + maxHeap.push({safeFactor[0], 0}); + + while (!maxHeap.empty()) { + auto [dist, node] = maxHeap.top(); maxHeap.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.push({dist2, node2}); + } + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [0, 1, 0, -1, 0]; + const minDist = grid; + + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + let maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + let safeFactor = new Array(N * N).fill(0); + safeFactor[0] = minDist[0][0]; + maxHeap.enqueue([safeFactor[0], 0]); + + while (!maxHeap.isEmpty()) { + let [dist, node] = maxHeap.dequeue().element; + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + let dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.enqueue([dist2, node2]); + } + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Multi Source BFS + Binary Search + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + def canReach(threshold): + q = deque([0]) + visited = [False] * (N * N) + visited[0] = True + + while q: + node = q.popleft() + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return True + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if (0 <= r2 < N and 0 <= c2 < N and not visited[node2] and + minDist[r2][c2] >= threshold + ): + visited[node2] = True + q.append(node2) + + return False + + l, r = 0, min(minDist[0][0], minDist[N - 1][N - 1]) + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + private static int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + int l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + + private boolean canReach(int[][] minDist, int N, int threshold) { + Queue q = new LinkedList<>(); + boolean[] visited = new boolean[N * N]; + q.offer(0); + visited[0] = true; + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.offer(node2); + } + } + } + return false; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[5] = {0, 1, 0, -1, 0}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + int l = 0, r = min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + +private: + bool canReach(vector>& minDist, int N, int threshold) { + queue q; + vector visited(N * N, false); + q.push(0); + visited[0] = true; + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + let q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + const canReach = (threshold) => { + q = new Queue([0]); + let visited = new Array(N * N).fill(false); + visited[0] = true; + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) return true; + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + }; + + let l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Breadth First Search (0-1 BFS) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + safeFactor = [-1] * (N * N) + res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]) + q.append(0) + + while q: + node = q.popleft() + r, c = divmod(node, N) + res = min(res, safeFactor[node]) + if r == N - 1 and c == N - 1: + break + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N and safeFactor[node2] == -1: + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]) + if safeFactor[node2] < res: + q.append(node2) + else: + q.appendleft(node2) + + return res +``` + +```java +public class Solution { + private static final int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Deque q = new ArrayDeque<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offerLast(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offerLast(r2 * N + c2); + } + } + } + + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + int res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.offerLast(0); + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + res = Math.min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.offerLast(node2); + } else { + q.offerFirst(node2); + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + constexpr int directions[5] = {0, 1, 0, -1, 0}; + + deque q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push_back(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push_back(r2 * N + c2); + } + } + } + + vector safeFactor(N * N, -1); + int res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]); + q.push_back(0); + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + res = min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.push_back(node2); + } else { + q.push_front(node2); + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + const q = new Deque(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.pushBack(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.pushBack(r2 * N + c2); + } + } + } + + let safeFactor = new Array(N * N).fill(-1); + let res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.pushBack(0); + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + res = Math.min(res, safeFactor[node]); + if (r === N - 1 && c === N - 1) { + break; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] === -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.pushBack(node2); + } else { + q.pushFront(node2); + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/find-unique-binary-string.md b/articles/find-unique-binary-string.md new file mode 100644 index 000000000..ea24db9f9 --- /dev/null +++ b/articles/find-unique-binary-string.md @@ -0,0 +1,685 @@ +## 1. Backtracking (Recursion) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = {s for s in nums} + + def backtrack(i, cur): + if i == len(nums): + res = "".join(cur) + return None if res in strSet else res + + res = backtrack(i + 1, cur) + if res: return res + + cur[i] = "1" + return backtrack(i + 1, cur) + + return backtrack(0, ["0" for _ in nums]) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + return backtrack(0, new char[nums.length], strSet, nums.length); + } + + private String backtrack(int i, char[] cur, Set strSet, int n) { + if (i == n) { + String res = new String(cur); + return strSet.contains(res) ? null : res; + } + + cur[i] = '0'; + String res = backtrack(i + 1, cur, strSet, n); + if (res != null) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + string cur(nums.size(), '0'); + return backtrack(0, cur, strSet, nums.size()); + } + +private: + string backtrack(int i, string& cur, unordered_set& strSet, int n) { + if (i == n) { + return strSet.count(cur) ? "" : cur; + } + + string res = backtrack(i + 1, cur, strSet, n); + if (!res.empty()) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + const backtrack = (i, cur) => { + if (i === n) { + const res = cur.join(""); + return strSet.has(res) ? null : res; + } + + let res = backtrack(i + 1, cur); + if (res) return res; + + cur[i] = "1"; + return backtrack(i + 1, cur); + }; + + return backtrack(0, Array(n).fill("0")); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Backtracking (Iteration) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + for num in range(1 << n): + res = bin(num)[2:].zfill(n) + if res not in strSet: + return res + + return "" +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + + for (int num = 0; num < (n + 1); num++) { + String res = String.format("%" + n + "s", + Integer.toBinaryString(num)).replace(' ', '0'); + if (!strSet.contains(res)) { + return res; + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + for (int num = 0; num < (n + 1); num++) { + string res = toBinaryString(num, n); + if (strSet.find(res) == strSet.end()) { + return res; + } + } + + return ""; + } + +private: + string toBinaryString(int num, int length) { + string res = ""; + for (int i = length - 1; i >= 0; i--) { + res += (num & (1 << i)) ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + for (let num = 0; num < (n + 1); num++) { + let res = num.toString(2).padStart(n, '0'); + if (!strSet.has(res)) { + return res; + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Cantor's Diagonal Argument + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + res = [] + for i in range(len(nums)): + if nums[i][i] == '0': + res.append('1') + else: + res.append('0') + return "".join(res) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < nums.length; i++) { + res.append(nums[i].charAt(i) == '0' ? '1' : '0'); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + string res; + for (int i = 0; i < nums.size(); i++) { + res += (nums[i][i] == '0') ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let res = []; + for (let i = 0; i < nums.length; i++) { + res.push(nums[i][i] === '0' ? '1' : '0'); + } + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 4. Randomization + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + while True: + res = "".join(random.choice("01") for _ in range(n)) + if res not in strSet: + return res +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + Random random = new Random(); + + while (true) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + res.append(random.nextBoolean() ? '1' : '0'); + } + String result = res.toString(); + if (!strSet.contains(result)) { + return result; + } + } + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + while (true) { + string res = ""; + for (int i = 0; i < n; i++) { + res += (rand() % 2) ? '1' : '0'; + } + if (strSet.find(res) == strSet.end()) { + return res; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + while (true) { + let res = Array.from({ length: n }, () => + Math.random() < 0.5 ? '0' : '1').join(""); + if (!strSet.has(res)) { + return res; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(∞)$ in worst case. +* Space complexity: $O(n)$ + +--- + +## 5. Trie + +::tabs-start + +```python +class Node: + def __init__(self): + self.children = [None, None] + + def contains_bit(self, bit: int) -> bool: + return self.children[bit] is not None + + def put(self, bit: int): + self.children[bit] = Node() + + def get(self, bit: int): + return self.children[bit] + +class Trie: + def __init__(self): + self.root = Node() + + def insert(self, s: str): + curr = self.root + for c in s: + bit = int(c) + if not curr.contains_bit(bit): + curr.put(bit) + curr = curr.get(bit) + + def search(self, res: str, curr) -> bool: + while curr.contains_bit(0) or curr.contains_bit(1): + if not curr.contains_bit(0): + res.append('0') + return True + if not curr.contains_bit(1): + res.append('1') + return True + + res.append('1') + curr = curr.get(1) + + return False + +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + trie = Trie() + for s in nums: + trie.insert(s) + + res = [] + trie.search(res, trie.root) + + while len(res) < len(nums): + res.append('1') + + return ''.join(res) +``` + +```java +class Node { + Node[] children; + + Node() { + this.children = new Node[2]; + } + + boolean containsBit(int bit) { + return this.children[bit] != null; + } + + void put(int bit) { + this.children[bit] = new Node(); + } + + Node get(int bit) { + return this.children[bit]; + } +} + +class Trie { + Node root; + + Trie() { + this.root = new Node(); + } + + void insert(String s) { + Node curr = root; + for (char c : s.toCharArray()) { + int bit = c - '0'; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + boolean search(StringBuilder res, Node curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.append('0'); + return true; + } + if (!curr.containsBit(1)) { + res.append('1'); + return true; + } + + res.append('1'); + curr = curr.get(1); + } + + return false; + } +} + +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Trie trie = new Trie(); + for (String s : nums) { + trie.insert(s); + } + + StringBuilder res = new StringBuilder(); + trie.search(res, trie.root); + + while (res.length() < nums.length) { + res.append('1'); + } + + return res.toString(); + } +} +``` + +```cpp +class Node { +public: + Node *children[2]; + + Node() { + this->children[0] = nullptr; + this->children[1] = nullptr; + } + + bool containsBit(int bit) { + return this->children[bit] != nullptr; + } + + void put(int bit) { + this->children[bit] = new Node(); + } + + Node* get(int bit) { + return this->children[bit]; + } +}; + +class Trie { +public: + Node* root; + + Trie() { + this->root = new Node(); + } + + void insert(const string& s) { + Node* curr = root; + for (char c : s) { + int bit = c - '0'; + if (!curr->containsBit(bit)) { + curr->put(bit); + } + curr = curr->get(bit); + } + } + + bool search(string& res, Node* curr) { + while (curr->containsBit(0) || curr->containsBit(1)) { + if (!curr->containsBit(0)) { + res += '0'; + return true; + } + if (!curr->containsBit(1)) { + res += '1'; + return true; + } + + res += '1'; + curr = curr->get(1); + } + + return false; + } +}; + +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + Trie trie; + for (const string& s : nums) { + trie.insert(s); + } + + string res; + trie.search(res, trie.root); + + while (res.length() < nums.size()) { + res += '1'; + } + + return res; + } +}; +``` + +```javascript +class Node { + constructor() { + this.children = [null, null]; + } + + /** + * @param {number} bit + * @return {boolean} + */ + containsBit(bit) { + return this.children[bit] !== null; + } + + /** + * @param {number} bit + */ + put(bit) { + this.children[bit] = new Node(); + } + + /** + * @param {number} bit + * @return {Node} + */ + get(bit) { + return this.children[bit]; + } +} + +class Trie { + constructor() { + this.root = new Node(); + } + + /** + * @param {string} s + */ + insert(s) { + let curr = this.root; + for (const c of s) { + let bit = c === '1' ? 1 : 0; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + /** + * @param {string[]} res + * @param {Node} curr + * @return {boolean} + */ + search(res, curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.push('0'); + return true; + } + if (!curr.containsBit(1)) { + res.push('1'); + return true; + } + + res.push('1'); + curr = curr.get(1); + } + return false; + } +} + +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let trie = new Trie(); + for (const s of nums) { + trie.insert(s); + } + + let res = []; + trie.search(res, trie.root); + + while (res.length < nums.length) { + res.push('1'); + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/furthest-building-you-can-reach.md b/articles/furthest-building-you-can-reach.md new file mode 100644 index 000000000..24ca0ef3b --- /dev/null +++ b/articles/furthest-building-you-can-reach.md @@ -0,0 +1,723 @@ +## 1. Brute Force (Greedy) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + n = len(heights) + + for i in range(1, n): + if ladders >= i: + continue + + diffs = [] + for j in range(i): + if heights[j + 1] > heights[j]: + diffs.append(heights[j + 1] - heights[j]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + + if brickSum > bricks: + return i - 1 + + return n - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int n = heights.length; + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + List diffs = new ArrayList<>(); + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.add(heights[j + 1] - heights[j]); + } + } + + Collections.sort(diffs); + long brickSum = 0; + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int n = heights.size(); + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + vector diffs; + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push_back(heights[j + 1] - heights[j]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let n = heights.length; + + for (let i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + let diffs = []; + for (let j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push(heights[j + 1] - heights[j]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search On Buildings + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + def canReach(mid): + diffs = [] + for i in range(mid): + if heights[i + 1] > heights[i]: + diffs.append(heights[i + 1] - heights[i]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + if brickSum > bricks: + return False + + return True + + l, r = ladders - 1, len(heights) - 1 + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(int[] heights, int mid, int bricks, int ladders) { + List diffs = new ArrayList<>(); + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.add(heights[i + 1] - heights[i]); + } + } + + Collections.sort(diffs); + int brickSum = 0; + + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + if (brickSum > bricks) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.size() - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector& heights, int mid, int bricks, int ladders) { + vector diffs; + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push_back(heights[i + 1] - heights[i]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let l = ladders - 1, r = heights.length - 1; + + const canReach = (mid) => { + let diffs = []; + for (let i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push(heights[i + 1] - heights[i]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + }; + + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search On Buildings (Optimal) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + diffs = [] + for i in range(1, len(heights)): + if heights[i] > heights[i - 1]: + diffs.append((heights[i] - heights[i - 1], i)) + + diffs.sort(reverse=True) + + def canReach(index): + useLadders = useBricks = 0 + for diff, i in diffs: + if i > index: + continue + + if useLadders < ladders: + useLadders += 1 + else: + useBricks += diff + if useBricks > bricks: + return False + return True + + l, r = 1, len(heights) - 1 + while l <= r: + mid = (l + r) >> 1 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + List diffs = new ArrayList<>(); + for (int i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.add(new int[]{heights[i] - heights[i - 1], i}); + } + } + + diffs.sort((a, b) -> Integer.compare(b[0], a[0])); + + int l = 1, r = heights.length - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(List diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long useBricks = 0; + for (int[] diff : diffs) { + int jump = diff[0], i = diff[1]; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + vector> diffs; + for (int i = 1; i < heights.size(); i++) { + if (heights[i] > heights[i - 1]) { + diffs.emplace_back(heights[i] - heights[i - 1], i); + } + } + + sort(diffs.rbegin(), diffs.rend()); // Sort in descending order + + int l = 1, r = heights.size() - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector>& diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long long useBricks = 0; + for (auto& diff : diffs) { + int jump = diff.first, i = diff.second; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let diffs = []; + for (let i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.push([heights[i] - heights[i - 1], i]); + } + } + + diffs.sort((a, b) => b[0] - a[0]); + + const canReach = (index) => { + let useLadders = 0, useBricks = 0; + for (let [diff, i] of diffs) { + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += diff; + if (useBricks > bricks) { + return false; + } + } + } + return true; + }; + + let l = 1, r = heights.length - 1; + while (l <= r) { + let mid = (l + r) >> 1; + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Max-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + heap = [] # Max heap of bricks used + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + bricks -= diff + heapq.heappush(heap, -diff) + + if bricks < 0: + if ladders == 0: + return i + ladders -= 1 + bricks += -heapq.heappop(heap) + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.offer(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.poll(); + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue maxHeap; + + for (int i = 0; i < heights.size() - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.push(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.top(); + maxHeap.pop(); + } + } + + return heights.size() - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const maxHeap = new MaxPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.enqueue(diff); + + if (bricks < 0) { + if (ladders === 0) return i; + ladders--; + bricks += maxHeap.dequeue().element; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Min-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + minHeap = [] + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + heapq.heappush(minHeap, diff) + if len(minHeap) > ladders: + bricks -= heapq.heappop(minHeap) + if bricks < 0: + return i + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue minHeap = new PriorityQueue<>(); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.offer(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.poll(); + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue, greater> minHeap; + + for (int i = 0; i < int(heights.size()) - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.push(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.top(); minHeap.pop(); + if (bricks < 0) return i; + } + } + + return int(heights.size()) - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const minHeap = new MinPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.enqueue(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.dequeue().element; + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/integer-to-roman.md b/articles/integer-to-roman.md new file mode 100644 index 000000000..6bb15bb30 --- /dev/null +++ b/articles/integer-to-roman.md @@ -0,0 +1,195 @@ +## 1. Math - I + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ] + + res = "" + for sym, val in reversed(symList): + count = num // val + if count: + res += sym * count + num %= val + + return res +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[][] symList = { + {"I", "1"}, {"IV", "4"}, {"V", "5"}, {"IX", "9"}, + {"X", "10"}, {"XL", "40"}, {"L", "50"}, {"XC", "90"}, + {"C", "100"}, {"CD", "400"}, {"D", "500"}, {"CM", "900"}, + {"M", "1000"} + }; + + StringBuilder res = new StringBuilder(); + for (int i = symList.length - 1; i >= 0; i--) { + String sym = symList[i][0]; + int val = Integer.parseInt(symList[i][1]); + int count = num / val; + if (count > 0) { + res.append(sym.repeat(count)); + num %= val; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + vector> symList = { + {"I", 1}, {"IV", 4}, {"V", 5}, {"IX", 9}, + {"X", 10}, {"XL", 40}, {"L", 50}, {"XC", 90}, + {"C", 100}, {"CD", 400}, {"D", 500}, {"CM", 900}, + {"M", 1000} + }; + + string res = ""; + for (int i = symList.size() - 1; i >= 0; i--) { + string sym = symList[i].first; + int val = symList[i].second; + int count = num / val; + if (count > 0) { + res.append(count, sym[0]); + if (sym.size() == 2) res.append(1, sym[1]); + num %= val; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ]; + + let res = ""; + for (let i = symList.length - 1; i >= 0; i--) { + const [sym, val] = symList[i]; + let count = Math.floor(num / val); + if (count > 0) { + res += sym.repeat(count); + num %= val; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math - II + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + thousands = ["", "M", "MM", "MMM"] + hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"] + tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"] + ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"] + + return ( + thousands[num // 1000] + + hundreds[(num % 1000) // 100] + + tens[(num % 100) // 10] + + ones[num % 10] + ) +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + string thousands[] = {"", "M", "MM", "MMM"}; + string hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + string tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + string ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const thousands = ["", "M", "MM", "MMM"]; + const hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; + const tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; + const ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; + + return thousands[Math.floor(num / 1000)] + + hundreds[Math.floor((num % 1000) / 100)] + + tens[Math.floor((num % 100) / 10)] + + ones[num % 10]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/is-graph-bipartite.md b/articles/is-graph-bipartite.md new file mode 100644 index 000000000..2db1c9d94 --- /dev/null +++ b/articles/is-graph-bipartite.md @@ -0,0 +1,611 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def dfs(i, c): + color[i] = c + for nei in graph[i]: + if color[nei] == c: + return False + if color[nei] == 0 and not dfs(nei, -c): + return False + return True + + for i in range(len(graph)): + if color[i] == 0 and not dfs(i, 1): + return False + return True +``` + +```java +public class Solution { + private int[] color; + + public boolean isBipartite(int[][] graph) { + int n = graph.length; + color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } + + private boolean dfs(int[][] graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + vector color; + + bool dfs(vector>& graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } + +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + color.assign(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + const dfs = (i, c) => { + color[i] = c; + for (let nei of graph[i]) { + if (color[nei] === c) { + return false; + } + if (color[nei] === 0 && !dfs(nei, -c)) { + return false; + } + } + return true; + }; + + for (let i = 0; i < n; i++) { + if (color[i] === 0 && !dfs(i, 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def bfs(i): + if color[i]: + return True + q = deque([i]) + color[i] = -1 + while q: + i = q.popleft() + for nei in graph[i]: + if color[i] == color[nei]: + return False + elif not color[nei]: + q.append(nei) + color[nei] = -1 * color[i] + return True + + for i in range(len(graph)): + if not bfs(i): + return False + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + Queue q = new LinkedList<>(); + q.offer(i); + color[i] = -1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.offer(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + queue q; + q.push(i); + color[i] = -1; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + let q = new Queue([i]); + color[i] = -1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of graph[node]) { + if (color[nei] === color[node]) { + return false; + } else if (color[nei] === 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + stack = [] + + for i in range(len(graph)): + if color[i] != 0: + continue + color[i] = -1 + stack.append(i) + while stack: + node = stack.pop() + for nei in graph[node]: + if color[node] == color[nei]: + return False + elif not color[nei]: + stack.append(nei) + color[nei] = -1 * color[node] + + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n); // Map node i -> odd=1, even=-1 + stack stack; + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + let stack = []; + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + color[i] = -1; + stack.push(i); + while (stack.length > 0) { + let node = stack.pop(); + for (let nei of graph[node]) { + if (color[node] === color[nei]) return false; + if (color[nei] === 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n)) + self.Size = [0] * n + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] > self.Size[pv]: + self.Parent[pv] = pu + elif self.Size[pu] < self.Size[pv]: + self.Parent[pu] = pv + else: + self.Parent[pv] = pu + self.Size[pu] += 1 + return True + +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + n = len(graph) + dsu = DSU(n) + + for node in range(n): + for nei in graph[node]: + if dsu.find(node) == dsu.find(nei): + return False + dsu.union(graph[node][0], nei) + + return True +``` + +```java +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 0; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +} + +class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + DSU dsu = new DSU(n); + + for (int node = 0; node < n; node++) { + for (int nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +```cpp +class DSU { +private: + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n); + Size.resize(n, 0); + for (int i = 0; i < n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +}; + +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + DSU dsu(n); + + for (int node = 0; node < n; node++) { + for (int& nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.unionSet(graph[node][0], nei); + } + } + return true; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let dsu = new DSU(n); + + for (let node = 0; node < n; node++) { + for (let nei of graph[node]) { + if (dsu.find(node) === dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/knight-dialer.md b/articles/knight-dialer.md new file mode 100644 index 000000000..9c87f4c13 --- /dev/null +++ b/articles/knight-dialer.md @@ -0,0 +1,946 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + def dfs(n, d): + if n == 0: + return 1 + + res = 0 + for j in jumps[d]: + res = (res + dfs(n - 1, j)) % mod + return res + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + const vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dfs = (n, d) => { + if (n === 0) return 1; + + let res = 0; + for (const next of jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[-1] * (n + 1) for _ in range(10)] + + def dfs(n, d): + if n == 0: + return 1 + if dp[d][n] != -1: + return dp[d][n] + + dp[d][n] = 0 + for j in jumps[d]: + dp[d][n] = (dp[d][n] + dfs(n - 1, j)) % mod + return dp[d][n] + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + private int[][] dp; + + public int knightDialer(int n) { + if (n == 1) return 10; + dp = new int[10][n + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + vector> dp; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + dp.assign(10, vector(n + 1, -1)); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(-1)); + + const dfs = (n, d) => { + if (n === 0) return 1; + if (dp[d][n] !== -1) return dp[d][n]; + + dp[d][n] = 0; + for (const next of jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[0] * (n + 1) for _ in range(10)] + + for d in range(10): + dp[d][0] = 1 + + for step in range(1, n): + for d in range(10): + dp[d][step] = sum(dp[j][step - 1] for j in jumps[d]) % mod + + return sum(dp[d][n - 1] for d in range(10)) % mod +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] dp = new int[10][n + 1]; + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> dp(10, vector(n + 1, 0)); + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(0)); + for (let d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (let step = 1; step < n; step++) { + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [1] * 10 + for step in range(n - 1): + next_dp = [0] * 10 + for d in range(10): + for j in jumps[d]: + next_dp[j] = (next_dp[j] + dp[d]) % mod + dp = next_dp + + res = 0 + for d in dp: + res = (res + d) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[] dp = new int[10]; + Arrays.fill(dp, 1); + + for (int step = 0; step < n - 1; step++) { + int[] nextDp = new int[10]; + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector dp(10, 1); + + for (int step = 0; step < n - 1; step++) { + vector nextDp(10, 0); + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + let dp = new Array(10).fill(1); + + for (let step = 0; step < n - 1; step++) { + let nextDp = new Array(10).fill(0); + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + return dp.reduce((res, d) => (res + d) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 10**9 + 7 + jumps = [1, 4, 2, 2] # [D, A, B, C] + + for _ in range(n - 1): + tmp = [0] * 4 + tmp[0] = jumps[3] + tmp[1] = 2 * jumps[2] + 2 * jumps[3] + tmp[2] = jumps[1] + tmp[3] = 2 * jumps[0] + jumps[1] + jumps = tmp + + return sum(jumps) % mod +``` + +```java +public class Solution { + public int knightDialer(int n) { + if (n == 1) return 10; + + int MOD = 1000000007; + long[] jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + long[] tmp = new long[4]; + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + long res = 0; + for (long num : jumps) { + res = (res + num) % MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + const int MOD = 1000000007; + vector jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + vector tmp(4); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return (jumps[0] + jumps[1] + jumps[2] + jumps[3]) % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const MOD = 1000000007; + let jumps = [1, 4, 2, 2]; // [D, A, B, C] + + for (let i = 0; i < n - 1; i++) { + let tmp = new Array(4).fill(0); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return jumps.reduce((sum, num) => (sum + num) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Matrix Exponentiation + +::tabs-start + +```python +class Matrix: + def __init__(self, size): + self.n = size + self.a = [[0] * size for _ in range(size)] + + def __mul__(self, other): + n = self.n + MOD = 10**9 + 7 + product = Matrix(n) + for i in range(n): + for j in range(n): + for k in range(n): + product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % MOD + return product + + +def matpow(mat, n, size): + res = Matrix(size) + for i in range(size): + res.a[i][i] = 1 # Identity matrix + + while n: + if n & 1: + res = res * mat + mat = mat * mat + n >>= 1 + + return res + + +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + MOD = 10**9 + 7 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + mat = Matrix(10) + for i in range(10): + for j in jumps[i]: + mat.a[i][j] = 1 + + res = matpow(mat, n - 1, 10) + + ans = sum(sum(res.a[i]) for i in range(10)) % MOD + return ans +``` + +```java +class Matrix { + int[][] a; + int size; + static final int MOD = 1_000_000_007; + + public Matrix(int size) { + this.size = size; + a = new int[size][size]; + } + + public Matrix multiply(Matrix other) { + Matrix product = new Matrix(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (int)((product.a[i][k] + (long) a[i][j] * other.a[j][k]) % MOD); + } + } + } + return product; + } +} + +public class Solution { + private Matrix matpow(Matrix mat, int n, int size) { + Matrix res = new Matrix(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if ((n & 1) == 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + n >>= 1; + } + return res; + } + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat = new Matrix(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix.MOD; + } + } + return ans; + } +} +``` + +```cpp +class Matrix { +public: + vector> a; + int size; + static const int MOD = 1'000'000'007; + + Matrix(int n) : size(n) { + a.assign(n, vector(n, 0)); + } + + Matrix operator*(const Matrix &other) const { + Matrix product(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (product.a[i][k] + 1LL * a[i][j] * other.a[j][k]) % MOD; + } + } + } + return product; + } +}; + +Matrix matpow(Matrix mat, int n, int size) { + Matrix res(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if (n & 1) res = res * mat; + mat = mat * mat; + n >>= 1; + } + return res; +} + +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix::MOD; + } + } + return ans; + } +}; +``` + +```javascript +class Matrix { + constructor(size) { + this.size = size; + this.a = Array.from({ length: size }, () => Array(size).fill(0)); + this.MOD = BigInt(1e9 + 7); + } + + multiply(other) { + const product = new Matrix(this.size); + for (let i = 0; i < this.size; i++) { + for (let j = 0; j < this.size; j++) { + let sum = BigInt(0); + for (let k = 0; k < this.size; k++) { + sum = (sum + BigInt(this.a[i][k]) * BigInt(other.a[k][j])) % this.MOD; + } + product.a[i][j] = Number(sum); + } + } + return product; + } +} + +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const matpow = (mat, exp, size) => { + let res = new Matrix(size); + for (let i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (exp > 0) { + if (exp & 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + exp >>= 1; + } + return res; + }; + + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const mat = new Matrix(10); + for (let i = 0; i < 10; i++) { + for (let j of jumps[i]) { + mat.a[i][j] = 1; + } + } + + const res = matpow(mat, n - 1, 10); + const mod = 1e9 + 7 + let ans = 0; + + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % mod; + } + } + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/largest-color-value-in-a-directed-graph.md b/articles/largest-color-value-in-a-directed-graph.md new file mode 100644 index 000000000..dcbf467a4 --- /dev/null +++ b/articles/largest-color-value-in-a-directed-graph.md @@ -0,0 +1,589 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + visit = [False] * n + def dfs(node, c): + if visit[node]: + return float("inf") + + visit[node] = True + clrCnt = 0 + for nei in adj[node]: + cur = dfs(nei, c) + if cur == float("inf"): + return cur + clrCnt = max(clrCnt, cur) + visit[node] = False + return clrCnt + (c == (ord(colors[node]) - ord('a'))) + + res = -1 + for i in range(n): + for c in range(26): + cnt = dfs(i, c) + if cnt == float("inf"): + return -1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == Integer.MAX_VALUE) return -1; + res = Math.max(res, cnt); + } + } + return res; + } + + private int dfs(int node, int c, String colors) { + if (visit[node]) return Integer.MAX_VALUE; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == Integer.MAX_VALUE) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors.charAt(node) - 'a') == c ? 1 : 0); + } +} +``` + +```cpp +class Solution { +public: + int n; + vector> adj; + vector visit; + + int largestPathValue(string colors, vector>& edges) { + n = colors.size(); + adj.assign(n, vector()); + visit.assign(n, false); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == 1e9) return -1; + res = max(res, cnt); + } + } + return res; + } + +private: + int dfs(int node, int c, string& colors) { + if (visit[node]) return 1e9; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == 1e9) return cur; + clrCnt = max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors[node] - 'a') == c ? 1 : 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const visit = new Array(n).fill(false); + const dfs = (node, c) => { + if (visit[node]) return Infinity; + + visit[node] = true; + let clrCnt = 0; + for (const nei of adj[node]) { + const cur = dfs(nei, c); + if (cur === Infinity) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + (c === colors.charCodeAt(node) - 97 ? 1 : 0); + }; + + let res = -1; + for (let i = 0; i < n; i++) { + for (let c = 0; c < 26; c++) { + const cnt = dfs(i, c); + if (cnt === Infinity) return -1; + res = Math.max(res, cnt); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E))$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + def dfs(node): + if node in path: + return float("inf") + if node in visit: + return 0 + + visit.add(node) + path.add(node) + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] = 1 + + for nei in adj[node]: + if dfs(nei) == float("inf"): + return float("inf") + for c in range(26): + count[node][c] = max( + count[node][c], + (1 if c == colorIndex else 0) + count[nei][c] + ) + + path.remove(node) + return 0 + + n, res = len(colors), 0 + visit, path = set(), set() + count = [[0] * 26 for _ in range(n)] + + for i in range(n): + if dfs(i) == float("inf"): + return -1 + res = max(res, max(count[i])) + + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit, path; + private int[][] count; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + this.path = new boolean[n]; + this.count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == Integer.MAX_VALUE) return -1; + for (int c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } + + private int dfs(int node, String colors) { + if (path[node]) return Integer.MAX_VALUE; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex] = 1; + + for (int nei : adj[node]) { + if (dfs(nei, colors) == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + for (int c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +} +``` + +```cpp +class Solution { +public: + int n, INF = 1e9; + vector> adj; + vector visit, path; + vector> count; + + int largestPathValue(string colors, vector>& edges) { + this->n = colors.size(); + adj.resize(n); + visit.assign(n, false); + path.assign(n, false); + count.assign(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == INF) return -1; + for (int c = 0; c < 26; c++) { + res = max(res, count[i][c]); + } + } + return res; + } + +private: + int dfs(int node, string& colors) { + if (path[node]) return INF; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex] = 1; + + for (int& nei : adj[node]) { + if (dfs(nei, colors) == INF) return INF; + for (int c = 0; c < 26; c++) { + count[node][c] = max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [src, dst] of edges) { + adj[src].push(dst); + } + + const visit = new Array(n).fill(false); + const path = new Array(n).fill(false); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + const dfs = (node) => { + if (path[node]) return Infinity; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex] = 1; + + for (const nei of adj[node]) { + if (dfs(nei) === Infinity) return Infinity; + for (let c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c === colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) === Infinity) return -1; + for (let c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + indegree = [0] * n + count = [[0] * 26 for _ in range(n)] + + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + q = deque() + for i in range(n): + if indegree[i] == 0: + q.append(i) + + visit = res = 0 + while q: + node = q.popleft() + visit += 1 + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] += 1 + res = max(res, count[node][colorIndex]) + + for nei in adj[node]: + for c in range(26): + count[nei][c] = max(count[nei][c], count[node][c]) + + indegree[nei] -= 1 + if indegree[nei] == 0: + q.append(nei) + + return res if visit == n else -1 +``` + +```java +public class Solution { + public int largestPathValue(String colors, int[][] edges) { + int n = colors.length(); + List[] adj = new ArrayList[n]; + int[] indegree = new int[n]; + int[][] count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + indegree[edge[1]]++; + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int visit = 0, res = 0; + while (!q.isEmpty()) { + int node = q.poll(); + visit++; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (int nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.add(nei); + } + } + } + + return visit == n ? res : -1; + } +} +``` + +```cpp +class Solution { +public: + int largestPathValue(string colors, vector>& edges) { + int n = colors.size(); + vector> adj(n); + vector indegree(n); + vector> count(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + int visit = 0, res = 0; + while (!q.empty()) { + int node = q.front();q.pop(); + visit++; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex]++; + res = max(res, count[node][colorIndex]); + + for (int& nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.push(nei); + } + } + } + + return visit == n ? res : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + const indegree = new Array(n).fill(0); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const q = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + q.push(i); + } + } + + let visit = 0, res = 0; + while (!q.isEmpty()) { + const node = q.pop(); + visit++; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (const nei of adj[node]) { + for (let c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] === 0) { + q.push(nei); + } + } + } + + return visit === n ? res : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/largest-divisible-subset.md b/articles/largest-divisible-subset.md new file mode 100644 index 000000000..c5d8fa381 --- /dev/null +++ b/articles/largest-divisible-subset.md @@ -0,0 +1,738 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} # (i, prevIndex) -> List + + def dfs(i, prevIndex): + if i == len(nums): + return [] + if (i, prevIndex) in cache: + return cache[(i, prevIndex)] + + res = dfs(i + 1, prevIndex) # Skip nums[i] + if prevIndex == -1 or nums[i] % nums[prevIndex] == 0: + tmp = [nums[i]] + dfs(i + 1, i) # Include nums[i] + res = tmp if len(tmp) > len(res) else res + + cache[(i, prevIndex)] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private List[][] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n][n + 1]; + return dfs(0, -1, nums); + } + + private List dfs(int i, int prevIndex, int[] nums) { + if (i == nums.length) return new ArrayList<>(); + if (cache[i][prevIndex + 1] != null) return cache[i][prevIndex + 1]; + + List res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(i + 1, i, nums)); + if (tmp.size() > res.size()) res = tmp; + } + + cache[i][prevIndex + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache = vector>>(n, vector>(n + 1)); + return dfs(0, -1, nums); + } + + vector dfs(int i, int prevIndex, vector& nums) { + if (i == nums.size()) return {}; + if (!cache[i][prevIndex + 1].empty()) return cache[i][prevIndex + 1]; + + vector res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(i + 1, i, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + if (tmp.size() > res.size()) res = tmp; + } + + return cache[i][prevIndex + 1] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const cache = new Map(); + + const dfs = (i, prevIndex) => { + if (i === nums.length) return []; + + let key = `${i},${prevIndex}`; + if (cache.has(key)) return cache.get(key); + + let res = dfs(i + 1, prevIndex); + if (prevIndex === -1 || nums[i] % nums[prevIndex] === 0) { + let tmp = [nums[i], ...dfs(i + 1, i)]; + if (tmp.length > res.length) res = tmp; + } + + cache.set(key, res); + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down) Space Optimized + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} + + def dfs(i): + if i in cache: + return cache[i] + + res = [nums[i]] + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dfs(j) + if len(tmp) > len(res): + res = tmp + + cache[i] = res + return res + + res = [] + for i in range(len(nums)): + tmp = dfs(i) + if len(tmp) > len(res): + res = tmp + return res +``` + +```java +public class Solution { + private List[] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n]; + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + private List dfs(int i, int[] nums) { + if (cache[i] != null) return cache[i]; + + List res = new ArrayList<>(); + res.add(nums[i]); + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(j, nums)); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +} +``` + +```cpp +class Solution { +private: + vector> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache.resize(n, vector()); + + vector res; + for (int i = 0; i < n; i++) { + vector tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + vector dfs(int i, vector& nums) { + if (!cache[i].empty()) return cache[i]; + + vector res = {nums[i]}; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(j, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const cache = new Array(n).fill(null); + + const dfs = (i) => { + if (cache[i] !== null) return cache[i]; + + let res = [nums[i]]; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dfs(j)]; + if (tmp.length > res.length) { + res = tmp; + } + } + } + return (cache[i] = res); + }; + + let res = []; + for (let i = 0; i < n; i++) { + let tmp = dfs(i); + if (tmp.length > res.length) { + res = tmp; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + dp = [[num] for num in nums] # dp[i] = longest start at i + res = [] + for i in range(len(nums) - 1, -1, -1): + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dp[j] + dp[i] = tmp if len(tmp) > len(dp[i]) else dp[i] + res = dp[i] if len(dp[i]) > len(res) else res + return res +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + List[] dp = new ArrayList[n]; + List res = new ArrayList<>(); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = new ArrayList<>(); + dp[i].add(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dp[j]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n); + vector res; + + for (int i = n - 1; i >= 0; i--) { + dp[i].push_back(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = dp[j]; + tmp.insert(tmp.begin(), nums[i]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const dp = new Array(n).fill(0).map(() => []); + let res = []; + + for (let i = n - 1; i >= 0; i--) { + dp[i] = [nums[i]]; + + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dp[j]]; + + if (tmp.length > dp[i].length) { + dp[i] = tmp; + } + } + } + if (dp[i].length > res.length) { + res = dp[i]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Top-Down) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[-1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + def dfs(i): + if dp[i][0] != -1: + return dp[i][0] + + dp[i][0] = 1 + for j in range(i + 1, n): + if nums[j] % nums[i] == 0: + length = dfs(j) + 1 + if length > dp[i][0]: + dp[i][0] = length + dp[i][1] = j + + return dp[i][0] + + max_len, start_index = 1, 0 + for i in range(n): + if dfs(i) > max_len: + max_len = dfs(i) + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + + return subset +``` + +```java +public class Solution { + private int[][] dp; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + + private int dfs(int i, int[] nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + dp.assign(n, vector(2, -1)); + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + +private: + int dfs(int i, vector& nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [-1, -1]); // dp[i] = [maxLen, prevIdx] + + const dfs = (i) => { + if (dp[i][0] !== -1) return dp[i][0]; + + dp[i][0] = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let length = dfs(j) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + }; + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) > maxLen) { + maxLen = dfs(i); + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + max_len, start_index = 1, 0 + + for i in range(n): + for j in range(i): + if nums[i] % nums[j] == 0 and dp[j][0] + 1 > dp[i][0]: + dp[i][0] = dp[j][0] + 1 + dp[i][1] = j + + if dp[i][0] > max_len: + max_len = dp[i][0] + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + return subset +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + int[][] dp = new int[n][2]; // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n, vector(2, -1)); // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [1, -1]); // dp[i] = [maxLen, prevIdx] + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (nums[i] % nums[j] === 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/largest-submatrix-with-rearrangements.md b/articles/largest-submatrix-with-rearrangements.md new file mode 100644 index 000000000..bebc4c94e --- /dev/null +++ b/articles/largest-submatrix-with-rearrangements.md @@ -0,0 +1,522 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for start_row in range(ROWS): + ones = deque(list(range(COLS))) + + for r in range(start_row, ROWS): + if not ones: + break + for _ in range(len(ones)): + c = ones.popleft() + if matrix[r][c] == 1: + ones.append(c) + + res = max(res, len(ones) * (r - start_row + 1)) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + Queue ones = new LinkedList<>(); + for (int c = 0; c < COLS; c++) { + ones.add(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.poll(); + if (matrix[r][c] == 1) { + ones.add(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + queue ones; + for (int c = 0; c < COLS; c++) { + ones.push(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.empty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.front(); ones.pop(); + if (matrix[r][c] == 1) { + ones.push(c); + } + } + + res = max(res, (int)ones.size() * (r - startRow + 1)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let startRow = 0; startRow < ROWS; startRow++) { + const ones = new Queue(); + for (let c = 0; c < COLS; c++) { + ones.push(c); + } + + for (let r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (let i = ones.size(); i > 0; i--) { + let c = ones.pop(); + if (matrix[r][c] === 1) { + ones.push(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prev_heights = [0] * COLS + + for r in range(ROWS): + heights = matrix[r][:] + for c in range(COLS): + if heights[c] > 0: + heights[c] += prev_heights[c] + + sorted_heights = sorted(heights, reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * sorted_heights[i]) + + prev_heights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + int[] prevHeights = new int[COLS]; + + for (int r = 0; r < ROWS; r++) { + int[] heights = Arrays.copyOf(matrix[r], COLS); + int[] sortedHgts = Arrays.copyOf(matrix[r], COLS); + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + Arrays.sort(sortedHgts); + for (int i = COLS - 1; i >= 0; i--) { + res = Math.max(res, (COLS - i) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + vector prevHeights(COLS); + + for (int r = 0; r < ROWS; r++) { + vector heights = matrix[r]; + vector sortedHgts = matrix[r]; + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sort(sortedHgts.begin(), sortedHgts.end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + let prevHeights = new Array(COLS).fill(0); + + for (let r = 0; r < ROWS; r++) { + let heights = [...matrix[r]]; + let sortedHgts = [...matrix[r]]; + + for (let c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sortedHgts.sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Greedy + Sorting (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r in range(1, ROWS): + for c in range(COLS): + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + + for r in range(ROWS): + matrix[r].sort(reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * matrix[r][i]) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + Arrays.sort(matrix[r]); + for (int i = 0; i < COLS; i++) { + res = Math.max(res, (COLS - i) * matrix[r][i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + sort(matrix[r].begin(), matrix[r].end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r = 1; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (let r = 0; r < ROWS; r++) { + matrix[r].sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algoirhtm. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Greedy + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prevHeights = [] + + for r in range(ROWS): + heights = [] + for c in prevHeights: + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + heights.append(c) + + for c in range(COLS): + if matrix[r][c] == 1: + heights.append(c) + + for i, c in enumerate(heights): + res = max(res, (i + 1) * matrix[r][c]) + + prevHeights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + List prevHeights = new ArrayList<>(); + + for (int r = 0; r < ROWS; r++) { + List heights = new ArrayList<>(); + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.add(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.add(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = Math.max(res, (i + 1) * matrix[r][heights.get(i)]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + vector prevHeights; + + for (int r = 0; r < ROWS; r++) { + vector heights; + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push_back(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.push_back(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0, prevHeights = []; + + for (let r = 0; r < ROWS; r++) { + let heights = []; + + for (let c of prevHeights) { + if (matrix[r][c] === 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push(c); + } + } + + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] === 1) { + heights.push(c); + } + } + + for (let i = 0; i < heights.length; i++) { + res = Math.max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/maximize-score-after-n-operations.md b/articles/maximize-score-after-n-operations.md new file mode 100644 index 000000000..bf90e9d55 --- /dev/null +++ b/articles/maximize-score-after-n-operations.md @@ -0,0 +1,629 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + N = len(nums) + visit = [False] * N + + def dfs(n): + if n > (N // 2): + return 0 + + res = 0 + for i in range(N): + if visit[i]: + continue + visit[i] = True + for j in range(i + 1, N): + if visit[j]: + continue + visit[j] = True + g = gcd(nums[i], nums[j]) + res = max(res, n * g + dfs(n + 1)) + visit[j] = False + visit[i] = False + + return res + + return dfs(1) +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int N = nums.length; + boolean[] visit = new boolean[N]; + return dfs(nums, visit, 1, N); + } + + private int dfs(int[] nums, boolean[] visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int N = nums.size(); + vector visit(N, false); + return dfs(nums, visit, 1, N); + } + +private: + int dfs(vector& nums, vector& visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const N = nums.length; + const visit = new Array(N).fill(false); + + const gcd = (a, b) => { + return b === 0 ? a : gcd(b, a % b); + }; + const dfs = (n) => { + if (n > N / 2) { + return 0; + } + + let res = 0; + for (let i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (let j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + let g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(n + 1)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + }; + + return dfs(1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 2. Bitmask DP (Top-Down) - I + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + cache = collections.defaultdict(int) + + def dfs(mask, op): + if mask in cache: + return cache[mask] + + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if (1 << i) & mask or (1 << j) & mask: + continue + + newMask = mask | (1 << i) | (1 << j) + score = op * math.gcd(nums[i], nums[j]) + cache[mask] = max( + cache[mask], + score + dfs(newMask, op + 1) + ) + + return cache[mask] + + return dfs(0, 1) +``` + +```java +public class Solution { + private Map cache; + + public int maxScore(int[] nums) { + cache = new HashMap<>(); + return dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (cache.containsKey(mask)) { + return cache.get(mask); + } + + int maxScore = 0; + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = Math.max(maxScore, score); + } + } + + cache.put(mask, maxScore); + return maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + return dfs(0, 1, nums); + } + +private: + unordered_map cache; + + int dfs(int mask, int op, vector& nums) { + if (cache.count(mask)) { + return cache[mask]; + } + + int maxScore = 0; + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = max(maxScore, score); + } + } + + return cache[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const cache = new Map(); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + const dfs = (mask, op) => { + if (cache.has(mask)) { + return cache.get(mask); + } + + let maxScore = 0; + const n = nums.length; + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + let score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1); + maxScore = Math.max(maxScore, score); + } + } + + cache.set(mask, maxScore); + return maxScore; + }; + + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n * \log m)$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 3. Bitmask DP (Top-Down) - II + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [-1] * (1 << n) + def dfs(mask, op): + if dp[mask] != -1: + return dp[mask] + + max_score = 0 + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + max_score = max( + max_score, + op * GCD[i][j] + dfs(new_mask, op + 1) + ) + + dp[mask] = max_score + return max_score + + return dfs(0, 1) +``` + +```java +public class Solution { + private int[][] GCD; + private int[] dp; + + public int maxScore(int[] nums) { + int n = nums.length; + GCD = new int[n][n]; + dp = new int[1 << n]; + Arrays.fill(dp, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return (int) dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.length; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < nums.length; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + GCD.assign(n, vector(n, 0)); + dp.assign(1 << n, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return dfs(0, 1, nums); + } + +private: + vector> GCD; + vector dp; + + int dfs(int mask, int op, vector& nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.size(); i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < nums.size(); j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + const dp = Array(1 << n).fill(-1); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dfs = (mask, op) => { + if (dp[mask] !== -1) return dp[mask]; + + let maxScore = 0; + for (let i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (let j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + const newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1) + ); + } + } + return dp[mask] = maxScore; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 4. Bitmask DP (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + N = 1 << n + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [0] * N + for mask in range(N - 1, -1, -1): + bits = bin(mask).count('1') + if bits % 2 == 1: + continue + op = bits // 2 + 1 + + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[new_mask]) + + return dp[0] +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int n = nums.length; + int N = 1 << n; + int[][] GCD = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + int[] dp = new int[N]; + for (int mask = N - 1; mask >= 0; mask--) { + int bits = Integer.bitCount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + int N = 1 << n; + vector> GCD(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = __gcd(nums[i], nums[j]); + } + } + + vector dp(N, 0); + for (int mask = N - 1; mask >= 0; mask--) { + int bits = __builtin_popcount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const N = 1 << n; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dp = Array(N).fill(0); + for (let mask = N - 1; mask >= 0; mask--) { + let bits = mask.toString(2).split("0").join("").length; + if (bits % 2 === 1) continue; + let op = bits / 2 + 1; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. \ No newline at end of file diff --git a/articles/maximum-element-after-decreasing-and-rearranging.md b/articles/maximum-element-after-decreasing-and-rearranging.md new file mode 100644 index 000000000..b7c01b0db --- /dev/null +++ b/articles/maximum-element-after-decreasing-and-rearranging.md @@ -0,0 +1,158 @@ +## 1. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + arr.sort() + prev = 0 + for num in arr: + prev = min(prev + 1, num) + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + Arrays.sort(arr); + int prev = 0; + for (int num : arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + sort(arr.begin(), arr.end()); + int prev = 0; + for (int num : arr) { + prev = min(prev + 1, num); + } + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + arr.sort((a, b) => a - b); + let prev = 0; + for (let num of arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + n = len(arr) + count = [0] * (n + 1) + + for num in arr: + count[min(num, n)] += 1 + + prev = 1 + for num in range(2, n + 1): + prev = min(prev + count[num], num) + + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + int n = arr.length; + int[] count = new int[n + 1]; + + for (int num : arr) { + count[Math.min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + int n = arr.size(); + vector count(n + 1, 0); + + for (int num : arr) { + count[min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = min(prev + count[num], num); + } + + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + let n = arr.length; + let count = new Array(n + 1).fill(0); + + for (let num of arr) { + count[Math.min(num, n)]++; + } + + let prev = 1; + for (let num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md new file mode 100644 index 000000000..f8cdea423 --- /dev/null +++ b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md @@ -0,0 +1,857 @@ +## 1. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = set() + + def overlap(charSet, s): + prev = set() + for c in s: + if c in charSet or c in prev: + return True + prev.add(c) + return False + + def backtrack(i): + if i == len(arr): + return len(charSet) + + res = 0 + if not overlap(charSet, arr[i]): + for c in arr[i]: + charSet.add(c) + res = backtrack(i + 1) + for c in arr[i]: + charSet.remove(c) + + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set charSet = new HashSet<>(); + return backtrack(0, arr, charSet); + } + + private boolean overlap(Set charSet, String s) { + Set prev = new HashSet<>(); + for (char c : s.toCharArray()) { + if (charSet.contains(c) || prev.contains(c)) { + return true; + } + prev.add(c); + } + return false; + } + + private int backtrack(int i, List arr, Set charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + for (char c : arr.get(i).toCharArray()) { + charSet.add(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet.remove(c); + } + } + + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set charSet; + return backtrack(0, arr, charSet); + } + +private: + bool overlap(unordered_set& charSet, const string& s) { + unordered_set prev; + for (char c : s) { + if (charSet.count(c) || prev.count(c)) { + return true; + } + prev.insert(c); + } + return false; + } + + int backtrack(int i, vector& arr, unordered_set& charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + for (char c : arr[i]) { + charSet.insert(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet.erase(c); + } + } + + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Set(); + + const overlap = (charSet, s) => { + let prev = new Set(); + for (const c of s) { + if (charSet.has(c) || prev.has(c)) { + return true; + } + prev.add(c); + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return charSet.size; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + for (const c of arr[i]) { + charSet.add(c); + } + res = backtrack(i + 1); + for (const c of arr[i]) { + charSet.delete(c); + } + } + + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 2. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = [False] * 26 + + def getIdx(c): + return ord(c) - ord('a') + + def overlap(charSet, s): + for i in range(len(s)): + c = getIdx(s[i]) + if charSet[c]: + for j in range(i): + charSet[getIdx(s[j])] = False + return True + charSet[c] = True + return False + + def backtrack(i): + if i == len(arr): + return 0 + + res = 0 + if not overlap(charSet, arr[i]): + res = len(arr[i]) + backtrack(i + 1) + for c in arr[i]: + charSet[getIdx(c)] = False + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + boolean[] charSet = new boolean[26]; + return backtrack(0, arr, charSet); + } + + private int getIdx(char c) { + return c - 'a'; + } + + private boolean overlap(boolean[] charSet, String s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s.charAt(i)); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s.charAt(j))] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + private int backtrack(int i, List arr, boolean[] charSet) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + res = arr.get(i).length() + backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + bool charSet[26] = {false}; + return backtrack(0, arr, charSet); + } + +private: + int getIdx(char c) { + return c - 'a'; + } + + bool overlap(bool charSet[], const string& s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s[i]); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + int backtrack(int i, vector& arr, bool charSet[]) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length() + backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet[getIdx(c)] = false; + } + } + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Array(26).fill(false); + + const getIdx = (c) => c.charCodeAt(0) - 'a'.charCodeAt(0); + + const overlap = (charSet, s) => { + for (let i = 0; i < s.length; i++) { + let c = getIdx(s[i]); + if (charSet[c]) { + for (let j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return 0; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length + backtrack(i + 1); + for (const c of arr[i]) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 3. Recursion (Bit Mask) - I + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + if i == len(A): + return 0 + + res = dfs(i + 1, subSeq) + + curSeq, length = A[i][0], A[i][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(i + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A.get(i)[0], length = A.get(i)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A[i].first, length = A[i].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + if (i === A.length) { + return 0; + } + + let res = dfs(i + 1, subSeq); + + let [curSeq, length] = A[i]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq)); + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n + 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 4. Recursion (Bit Mask) - II + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + res = 0 + for j in range(i, len(A)): + curSeq, length = A[j][0], A[j][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(j + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A.get(j)[0], length = A.get(j)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A[j].first, length = A[j].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + let res = 0; + for (let j = i; j < A.length; j++) { + let [curSeq, length] = A[j]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq)); + } + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + dp = {0} + res = 0 + + for s in arr: + cur = 0 + valid = True + + for c in s: + bit = 1 << (ord(c) - ord('a')) + if cur & bit: + valid = False + break + cur |= bit + + if not valid: + continue + + next_dp = dp.copy() + for seq in dp: + if (seq & cur) or (seq | cur) in dp: + continue + next_dp.add(seq | cur) + res = max(res, bin(seq | cur).count('1')) + dp = next_dp + + return res +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set dp = new HashSet<>(); + dp.add(0); + int res = 0; + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + int bit = 1 << (c - 'a'); + if ((cur & bit) != 0) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + Set next_dp = new HashSet<>(dp); + for (int seq : dp) { + if ((seq & cur) != 0 || next_dp.contains(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, Integer.bitCount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set dp; + dp.insert(0); + int res = 0; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + int bit = 1 << (c - 'a'); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + unordered_set next_dp(dp); + for (int seq : dp) { + if ((seq & cur) || next_dp.count(seq | cur)) { + continue; + } + next_dp.insert(seq | cur); + res = max(res, __builtin_popcount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let dp = new Set(); + dp.add(0); + let res = 0; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + let bit = 1 << (c.charCodeAt(0) - 97); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + let next_dp = new Set(dp); + for (let seq of dp) { + if ((seq & cur) || next_dp.has(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, (seq | cur).toString(2).split('0').join('').length); + } + dp = next_dp; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. \ No newline at end of file diff --git a/articles/maximum-length-of-pair-chain.md b/articles/maximum-length-of-pair-chain.md new file mode 100644 index 000000000..2855ede51 --- /dev/null +++ b/articles/maximum-length-of-pair-chain.md @@ -0,0 +1,556 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + + def dfs(i, j): + if i == n: + return 0 + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, j): + if i == n: + return 0 + if dp[i][j + 1] != -1: + return dp[i][j + 1] + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + dp[i][j + 1] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + dp = new int[n][n + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + dp = vector>(n, vector(n + 1, -1)); + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + let dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + if (dp[i][j + 1] !== -1) { + return dp[i][j + 1]; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + dp[i][j + 1] = res; + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [1] * n + + for i in range(n): + for j in range(i): + if pairs[j][1] < pairs[i][0]: + dp[i] = max(dp[i], dp[j] + 1) + + return max(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Arrays.stream(dp).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + vector dp(n, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + let n = pairs.length; + pairs.sort((a, b) => a[1] - b[1]); + let dp = new Array(n).fill(1); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bianry Search) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda x: x[0]) + dp = [] + + for a, b in pairs: + pos = bisect_left(dp, a) + if pos == len(dp): + dp.append(b) + else: + dp[pos] = min(dp[pos], b) + + return len(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, Comparator.comparingInt(a -> a[0])); + List dp = new ArrayList<>(); + + for (int[] pair : pairs) { + int pos = binarySearch(dp, pair[0]); + if (pos == dp.size()) { + dp.add(pair[1]); + } else { + dp.set(pos, Math.min(dp.get(pos), pair[1])); + } + } + + return dp.size(); + } + + private int binarySearch(List dp, int target) { + int left = 0, right = dp.size() - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (dp.get(mid) < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end()); + vector dp; + + for (auto& pair : pairs) { + auto it = lower_bound(dp.begin(), dp.end(), pair[0]); + if (it == dp.end()) { + dp.push_back(pair[1]); + } else { + *it = min(*it, pair[1]); + } + } + + return dp.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[0] - b[0]); + let dp = []; + + const binarySearch = (target) => { + let left = 0, right = dp.length - 1; + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + }; + + for (let i = 0; i < pairs.length; i++) { + let pos = binarySearch(pairs[i][0]); + if (pos === dp.length) { + dp.push(pairs[i][1]); + } else { + dp[pos] = Math.min(dp[pos], pairs[i][1]); + } + } + + return dp.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda p: p[1]) + length = 1 + end = pairs[0][1] + + for i in range(1, len(pairs)): + if end < pairs[i][0]: + length += 1 + end = pairs[i][1] + + return length +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int length = 1; + int end = pairs[0][1]; + + for (int i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + int length = 1, end = pairs[0][1]; + + for (int i = 1; i < pairs.size(); i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let length = 1; + let end = pairs[0][1]; + + for (let i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/maximum-nesting-depth-of-the-parentheses.md b/articles/maximum-nesting-depth-of-the-parentheses.md new file mode 100644 index 000000000..f18d24a7f --- /dev/null +++ b/articles/maximum-nesting-depth-of-the-parentheses.md @@ -0,0 +1,305 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + + def dfs(i): + nonlocal res + if i == len(s): + return 0 + + cur = dfs(i + 1) + if s[i] == '(': + cur += 1 + elif s[i] == ')': + cur -= 1 + + res = max(res, abs(cur)) + return cur + + dfs(0) + return res +``` + +```java +public class Solution { + private int res = 0; + + public int maxDepth(String s) { + dfs(s, 0); + return res; + } + + private int dfs(String s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s.charAt(i) == '(') { + cur += 1; + } else if (s.charAt(i) == ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + } +} +``` + +```cpp +class Solution { +private: + int res = 0; + + int dfs(const string& s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s[i] == '(') { + cur += 1; + } else if (s[i] == ')') { + cur -= 1; + } + + res = max(res, abs(cur)); + return cur; + } + +public: + int maxDepth(string s) { + dfs(s, 0); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + + const dfs = (i) => { + if (i === s.length) { + return 0; + } + + let cur = dfs(i + 1); + if (s[i] === '(') { + cur += 1; + } else if (s[i] === ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + }; + + dfs(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res, stack = 0, [] + + for c in s: + if c == "(": + stack.append(c) + res = max(res, len(stack)) + elif c == ")": + stack.pop() + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0; + Stack stack = new Stack<>(); + + for (char c : s.toCharArray()) { + if (c == '(') { + stack.push(c); + res = Math.max(res, stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0; + stack stack; + + for (char c : s) { + if (c == '(') { + stack.push(c); + res = max(res, (int)stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + let stack = []; + + for (let c of s) { + if (c === '(') { + stack.push(c); + res = Math.max(res, stack.length); + } else if (c === ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + cur = 0 + + for c in s: + if c == "(": + cur += 1 + elif c == ")": + cur -= 1 + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0, cur = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0, cur = 0; + + for (char c : s) { + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0, cur = 0; + + for (let c of s) { + if (c === '(') { + cur++; + } else if (c === ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-points-you-can-obtain-from-cards.md b/articles/maximum-points-you-can-obtain-from-cards.md new file mode 100644 index 000000000..8d1b9294a --- /dev/null +++ b/articles/maximum-points-you-can-obtain-from-cards.md @@ -0,0 +1,462 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + res = 0 + + for left in range(k + 1): + leftSum = sum(cardPoints[:left]) + rightSum = sum(cardPoints[n - (k - left):]) + res = max(res, leftSum + rightSum) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = max(res, leftSum + rightSum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let res = 0; + + for (let left = 0; left <= k; left++) { + let leftSum = 0; + for (let i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + let rightSum = 0; + for (let i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. + +--- + +## 2. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + cardPoints[i] + + suffix = [0] * (n + 1) + for i in range(n - 1, -1, -1): + suffix[i] = suffix[i + 1] + cardPoints[i] + + res = 0 + for left in range(k + 1): + right = k - left + res = max(res, prefix[left] + suffix[n - right]) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + + int[] prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + int[] suffix = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + + vector prefix(n + 1, 0); + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + vector suffix(n + 1, 0); + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + + let prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + let suffix = new Array(n + 1).fill(0); + for (let i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + let res = 0; + for (let left = 0; left <= k; left++) { + let right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (Minimum Sum Window) + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + windowSize = n - k + + if windowSize == 0: + return sum(cardPoints) + + total = 0 + minWindowSum = float("inf") + curSum = 0 + + for i in range(n): + total += cardPoints[i] + curSum += cardPoints[i] + if i >= windowSize - 1: + minWindowSum = min(minWindowSum, curSum) + curSum -= cardPoints[i - windowSize + 1] + + return total - minWindowSum +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int windowSize = n - k; + + if (windowSize == 0) { + int sum = 0; + for (int num : cardPoints) sum += num; + return sum; + } + + int total = 0; + int minWindowSum = Integer.MAX_VALUE; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int windowSize = n - k; + + if (windowSize == 0) { + return accumulate(cardPoints.begin(), cardPoints.end(), 0); + } + + int total = 0; + int minWindowSum = INT_MAX; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let windowSize = n - k; + + if (windowSize === 0) { + return cardPoints.reduce((sum, num) => sum + num, 0); + } + + let total = 0; + let minWindowSum = Infinity; + let curSum = 0; + + for (let i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + l, r = 0, len(cardPoints) - k + total = sum(cardPoints[r:]) + res = total + + while r < len(cardPoints): + total += cardPoints[l] - cardPoints[r] + res = max(res, total) + l += 1 + r += 1 + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int l = 0, r = cardPoints.length - k; + int total = 0; + + for (int i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int l = 0, r = cardPoints.size() - k; + int total = 0; + + for (int i = r; i < cardPoints.size(); i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.size()) { + total += cardPoints[l] - cardPoints[r]; + res = max(res, total); + l++; + r++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let l = 0, r = cardPoints.length - k; + let total = 0; + + for (let i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + let res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. \ No newline at end of file diff --git a/articles/maximum-score-of-a-good-subarray.md b/articles/maximum-score-of-a-good-subarray.md new file mode 100644 index 000000000..f8b6fbdc0 --- /dev/null +++ b/articles/maximum-score-of-a-good-subarray.md @@ -0,0 +1,729 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k + 1): + minEle = nums[i] + for j in range(i, n): + minEle = min(minEle, nums[j]) + if j >= k: + res = max(res, minEle * (j - i + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = min(minEle, nums[j]); + if (j >= k) { + res = max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i <= k; i++) { + let minEle = nums[i]; + for (let j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + arr = nums[:] + + for i in range(k - 1, -1, -1): + arr[i] = min(arr[i], arr[i + 1]) + for i in range(k + 1, n): + arr[i] = min(arr[i], arr[i - 1]) + + left_arr = arr[:k+1] + right_arr = arr[k:] + + def find_right(target): + lo, hi = 0, len(right_arr) - 1 + pos = 0 + while lo <= hi: + mid = (lo + hi) // 2 + if right_arr[mid] >= target: + pos = mid + lo = mid + 1 + else: + hi = mid - 1 + return pos + + for minVal in set(arr): + l = bisect_left(left_arr, minVal) + r = find_right(minVal) + res = max(res, minVal * (k - l + 1 + r)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + int[] arr = Arrays.copyOf(nums, n); + Set candidates = new HashSet<>(); + candidates.add(arr[k]); + + for (int i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + candidates.add(arr[i]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + candidates.add(arr[i]); + } + + int[] leftArr = Arrays.copyOfRange(arr, 0, k + 1); + int[] rightArr = Arrays.copyOfRange(arr, k, n); + + for (int minVal : candidates) { + int l = findLeft(leftArr, minVal); + int r = findRight(rightArr, minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } + + private int findLeft(int[] arr, int target) { + int lo = 0, hi = arr.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + private int findRight(int[] arr, int target) { + int lo = 0, hi = arr.length - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + vector arr = nums; + + for (int i = k - 1; i >= 0; i--) { + arr[i] = min(arr[i], arr[i + 1]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = min(arr[i], arr[i - 1]); + } + + vector leftArr(arr.begin(), arr.begin() + k + 1); + vector rightArr(arr.begin() + k, arr.end()); + + set candidates(arr.begin(), arr.end()); + for (int minVal : candidates) { + int l = lower_bound(leftArr.begin(), leftArr.end(), minVal) - leftArr.begin(); + int r = findRight(rightArr, minVal); + res = max(res, minVal * (k - l + 1 + r)); + } + return res; + } + +private: + int findRight(vector& arr, int target) { + int lo = 0, hi = arr.size() - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let arr = [...nums]; + + for (let i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + } + for (let i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + } + + let leftArr = arr.slice(0, k + 1); + let rightArr = arr.slice(k); + + const findLeft = (target) => { + let lo = 0, hi = leftArr.length - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (leftArr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = 0, hi = rightArr.length - 1, pos = 0; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (rightArr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + }; + + let candidates = [...new Set(arr)]; + for (let minVal of candidates) { + let l = findLeft(minVal); + let r = findRight(minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k - 1, -1, -1): + nums[i] = min(nums[i], nums[i + 1]) + for i in range(k + 1, n): + nums[i] = min(nums[i], nums[i - 1]) + + def find_left(target): + lo, hi = 0, k + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] < target: + lo = mid + 1 + else: + hi = mid - 1 + return lo + + def find_right(target): + lo, hi = k, n - 1 + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] >= target: + lo = mid + 1 + else: + hi = mid - 1 + return hi + + for minVal in set(nums): + i = find_left(minVal) + j = find_right(minVal) + res = max(res, minVal * (j - i + 1)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + Set candidates = new TreeSet<>(); + for (int num : nums) { + candidates.add(num); + } + + for (int minVal : candidates) { + int i = findLeft(nums, k, minVal); + int j = findRight(nums, k, minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } + + int findLeft(int[] nums, int k, int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + int findRight(int[] nums, int k, int target) { + int lo = k, hi = nums.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = min(nums[i], nums[i - 1]); + } + + auto findLeft = [&](int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + auto findRight = [&](int target) { + int lo = k, hi = n - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + set candidates(nums.begin(), nums.end()); + for (int minVal : candidates) { + int i = findLeft(minVal); + int j = findRight(minVal); + res = max(res, minVal * (j - i + 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (let i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + const findLeft = (target) => { + let lo = 0, hi = k; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = k, hi = n - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + let candidates = new Set(nums); + for (let minVal of candidates) { + let i = findLeft(minVal); + let j = findRight(minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonic Stack + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + stack = [] + + for i in range(n + 1): + while stack and (i == n or nums[stack[-1]] >= nums[i]): + mini = nums[stack.pop()] + j = stack[-1] if stack else -1 + if j < k < i: + res = max(res, mini * (i - j - 1)) + stack.append(i) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + Stack stack = new Stack<>(); + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || nums[stack.peek()] >= nums[i])) { + int mini = nums[stack.pop()]; + int j = stack.isEmpty() ? -1 : stack.peek(); + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + stack stk; + + for (int i = 0; i <= n; i++) { + while (!stk.empty() && (i == n || nums[stk.top()] >= nums[i])) { + int mini = nums[stk.top()]; + stk.pop(); + int j = stk.empty() ? -1 : stk.top(); + if (j < k && k < i) { + res = max(res, mini * (i - j - 1)); + } + } + stk.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let stack = []; + + for (let i = 0; i <= n; i++) { + while (stack.length && (i === n || nums[stack[stack.length - 1]] >= nums[i])) { + let mini = nums[stack.pop()]; + let j = stack.length ? stack[stack.length - 1] : -1; + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + l = r = k + res = nums[k] + cur_min = nums[k] + + while l > 0 or r < len(nums) - 1: + left = nums[l - 1] if l > 0 else 0 + right = nums[r + 1] if r < len(nums) - 1 else 0 + + if left > right: + l -= 1 + cur_min = min(cur_min, left) + else: + r += 1 + cur_min = min(cur_min, right) + + res = max(res, cur_min * (r - l + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.length; + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.size(); + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = min(curMin, left); + } else { + r++; + curMin = min(curMin, right); + } + + res = max(res, curMin * (r - l + 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let l = k, r = k; + let res = nums[k]; + let curMin = nums[k]; + let n = nums.length; + + while (l > 0 || r < n - 1) { + let left = (l > 0) ? nums[l - 1] : 0; + let right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-score-words-formed-by-letters.md b/articles/maximum-score-words-formed-by-letters.md new file mode 100644 index 000000000..f9b510481 --- /dev/null +++ b/articles/maximum-score-words-formed-by-letters.md @@ -0,0 +1,676 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + def can_form_word(w, letter_cnt): + word_cnt = Counter(w) + for c in word_cnt: + if word_cnt[c] > letter_cnt[c]: + return False + return True + + def get_score(w): + res = 0 + for c in w: + res += score[ord(c) - ord('a')] + return res + + letter_cnt = Counter(letters) + + def backtrack(i): + if i == len(words): + return 0 + + res = backtrack(i + 1) # skip + if can_form_word(words[i], letter_cnt): # include (when possible) + for c in words[i]: + letter_cnt[c] -= 1 + res = max(res, get_score(words[i]) + backtrack(i + 1)) + for c in words[i]: + letter_cnt[c] += 1 + + return res + + return backtrack(0) +``` + +```java +public class Solution { + int[] letterCnt; + int[] score; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + this.score = score; + this.letterCnt = new int[26]; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0, words); + } + + private int canFormWord(String word) { + int[] wordCnt = new int[26]; + for (char c : word.toCharArray()) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return 0; + } + } + return 1; + } + + private int getScore(String word) { + int res = 0; + for (char c : word.toCharArray()) { + res += score[c - 'a']; + } + return res; + } + + private int backtrack(int i, String[] words) { + if (i == words.length) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + if (canFormWord(words[i]) == 1) { // include (when possible) + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1, words)); + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector score; + vector words; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + this->words = words; + this->score = score; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0); + } + + bool canFormWord(string& word) { + vector wordCnt(26, 0); + for (char c : word) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return false; + } + } + return true; + } + + int getScore(string& word) { + int res = 0; + for (char c : word) { + res += score[c - 'a']; + } + return res; + } + + int backtrack(int i) { + if (i == words.size()) { + return 0; + } + + int res = backtrack(i + 1); // skip + if (canFormWord(words[i])) { // include (when possible) + for (char c : words[i]) { + letterCnt[c - 'a']--; + } + res = max(res, getScore(words[i]) + backtrack(i + 1)); + for (char c : words[i]) { + letterCnt[c - 'a']++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + const canFormWord = (w, letterCnt) => { + let wordCnt = new Array(26).fill(0); + for (let c of w) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordCnt[idx]++; + if (wordCnt[idx] > letterCnt[idx]) { + return false; + } + } + return true; + }; + + const getScore = (w) => { + let res = 0; + for (let c of w) { + res += score[c.charCodeAt(0) - 'a'.charCodeAt(0)]; + } + return res; + }; + + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const backtrack = (i) => { + if (i === words.length) { + return 0; + } + + let res = backtrack(i + 1); // skip + + if (canFormWord(words[i], letterCnt)) { // include (when possible) + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1)); + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n * (w + m) + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 2. Bactracking + Precomputation + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + def backtrack(i): + if i == n: + return 0 + + res = backtrack(i + 1) # skip + can_include = all(word_freqs[i][j] <= letter_cnt[j] for j in range(26)) + + if can_include: # include (when possible) + for j in range(26): + letter_cnt[j] -= word_freqs[i][j] + res = max(res, word_scores[i] + backtrack(i + 1)) + for j in range(26): + letter_cnt[j] += word_freqs[i][j] + + return res + + return backtrack(0) +``` + +```java +public class Solution { + private int[] letterCnt = new int[26]; + private int[] wordScores; + private int[][] wordFreqs; + private int n; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + Arrays.fill(letterCnt, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.length; + wordScores = new int[n]; + wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + + private int backtrack(int i, String[] words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + boolean canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector wordScores; + vector> wordFreqs; + int n; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + fill(letterCnt.begin(), letterCnt.end(), 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.size(); + wordScores = vector(n, 0); + wordFreqs = vector>(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + +private: + int backtrack(int i, vector& words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + bool canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + const backtrack = (i) => { + if (i === n) { + return 0; + } + + let res = backtrack(i + 1); // skip + let canInclude = true; + + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (let j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1)); + for (let j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 3. Backtracking (Bit Mask) + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + res = 0 + + for mask in range(1 << n): + cur_score = 0 + cur_letter_cnt = letter_cnt[:] + valid = True + + for i in range(n): + if mask & (1 << i): + for j in range(26): + if word_freqs[i][j] > cur_letter_cnt[j]: + valid = False + break + if not valid: + break + + for j in range(26): + cur_letter_cnt[j] -= word_freqs[i][j] + + cur_score += word_scores[i] + + if valid: + res = max(res, cur_score) + + return res +``` + +```java +public class Solution { + public int maxScoreWords(String[] words, char[] letters, int[] score) { + int[] letterCnt = new int[26]; + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.length; + int[] wordScores = new int[n]; + int[][] wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + int[] curLetterCnt = Arrays.copyOf(letterCnt, 26); + boolean valid = true; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScoreWords(vector& words, vector& letters, vector& score) { + vector letterCnt(26, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.size(); + vector wordScores(n, 0); + vector> wordFreqs(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + vector curLetterCnt = letterCnt; + bool valid = true; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = max(res, curScore); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + let res = 0; + for (let mask = 0; mask < (1 << n); mask++) { + let curScore = 0; + let curLetterCnt = [...letterCnt]; + let valid = true; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) { + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (let j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. \ No newline at end of file diff --git a/articles/minimize-maximum-of-array.md b/articles/minimize-maximum-of-array.md new file mode 100644 index 000000000..b851e40b5 --- /dev/null +++ b/articles/minimize-maximum-of-array.md @@ -0,0 +1,210 @@ +## 1. Binary Search + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + def isValid(maxVal): + prefix_sum = 0 + for i in range(len(nums)): + prefix_sum += nums[i] + if prefix_sum > maxVal * (i + 1): + return False + return True + + left, right = 0, max(nums) + while left < right: + mid = left + (right - left) // 2 + if isValid(mid): + right = mid + else: + left = mid + 1 + + return left +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int left = 0, right = 0; + for (int num : nums) { + right = Math.max(right, num); + } + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + + private boolean isValid(int[] nums, int maxVal) { + long prefixSum = 0; + for (int i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > (long) maxVal * (i + 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int left = 0, right = *max_element(nums.begin(), nums.end()); + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + +private: + bool isValid(vector& nums, int maxVal) { + long long prefixSum = 0; + for (int i = 0; i < nums.size(); i++) { + prefixSum += nums[i]; + if (prefixSum > (long long)maxVal * (i + 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + const isValid = (maxVal) => { + let prefixSum = 0; + for (let i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > maxVal * (i + 1)) { + return false; + } + } + return true; + }; + + let left = 0, right = Math.max(...nums); + while (left < right) { + let mid = left + Math.floor((right - left) / 2); + if (isValid(mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 2. Prefix Sum + Greedy + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + res = total = nums[0] + + for i in range(1, len(nums)): + total += nums[i] + res = max(res, math.ceil(total / (i + 1))) + + return res +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int res = nums[0]; + long total = nums[0]; + + for (int i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, (int) Math.ceil((double) total / (i + 1))); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int res = nums[0]; + long long total = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + total += nums[i]; + res = max(res, (int)ceil((double)total / (i + 1))); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + let res = nums[0]; + let total = nums[0]; + + for (let i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, Math.ceil(total / (i + 1))); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-deletions-to-make-character-frequencies-unique.md b/articles/minimum-deletions-to-make-character-frequencies-unique.md new file mode 100644 index 000000000..60c9564fa --- /dev/null +++ b/articles/minimum-deletions-to-make-character-frequencies-unique.md @@ -0,0 +1,348 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + used_freq = set() + res = 0 + for freq in count: + while freq > 0 and freq in used_freq: + freq -= 1 + res += 1 + used_freq.add(freq) + + return res +``` + +```java +class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Set usedFreq = new HashSet<>(); + int res = 0; + + for (int freq : count) { + while (freq > 0 && usedFreq.contains(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + unordered_set usedFreq; + int res = 0; + + for (int& freq : count) { + while (freq > 0 && usedFreq.count(freq)) { + freq--; + res++; + } + usedFreq.insert(freq); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let usedFreq = new Set(); + let res = 0; + + for (let freq of count) { + while (freq > 0 && usedFreq.has(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + freq = Counter(s) + maxHeap = [-f for f in freq.values()] + heapq.heapify(maxHeap) + + res = 0 + while len(maxHeap) > 1: + top = -heapq.heappop(maxHeap) + if top == -maxHeap[0]: + if top - 1 > 0: + heapq.heappush(maxHeap, -(top - 1)) + res += 1 + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + Map freq = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + freq.put(c, freq.getOrDefault(c, 0) + 1); + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + maxHeap.addAll(freq.values()); + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.poll(); + if (top == maxHeap.peek()) { + if (top - 1 > 0) { + maxHeap.add(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + unordered_map freq; + for (char& c : s) { + freq[c]++; + } + + priority_queue maxHeap; + for (auto& f : freq) { + maxHeap.push(f.second); + } + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.top(); + maxHeap.pop(); + if (top == maxHeap.top()) { + if (top - 1 > 0) { + maxHeap.push(top - 1); + } + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let freq = new Map(); + for (let c of s) { + freq.set(c, (freq.get(c) || 0) + 1); + } + + const maxHeap = new MaxPriorityQueue(); + for (let value of freq.values()) { + maxHeap.enqueue(value); + } + + let res = 0; + while (maxHeap.size() > 1) { + let top = maxHeap.dequeue().element; + if (maxHeap.front().element === top) { + if (top - 1 > 0) { + maxHeap.enqueue(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2 \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + count.sort(reverse=True) + res = 0 + maxAllowedFreq = count[0] + + for freq in count: + if freq > maxAllowedFreq: + res += freq - maxAllowedFreq + freq = maxAllowedFreq + maxAllowedFreq = max(0, freq - 1) + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Arrays.sort(count); + int res = 0; + int maxAllowedFreq = count[25]; + + for (int i = 25; i >= 0; i--) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + sort(count.begin(), count.end(), greater()); + int res = 0; + int maxAllowedFreq = count[0]; + + for (int& freq : count) { + if (freq > maxAllowedFreq) { + res += freq - maxAllowedFreq; + freq = maxAllowedFreq; + } + maxAllowedFreq = max(0, freq - 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + count.sort((a, b) => b - a); + + let res = 0; + let maxAllowedFreq = count[0]; + + for (let i = 0; i < 26; i++) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. \ No newline at end of file diff --git a/articles/minimum-number-of-arrows-to-burst-balloons.md b/articles/minimum-number-of-arrows-to-burst-balloons.md new file mode 100644 index 000000000..195e1e3e0 --- /dev/null +++ b/articles/minimum-number-of-arrows-to-burst-balloons.md @@ -0,0 +1,183 @@ +## 1. Greedy (Sort By Start Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort() + res, prevEnd = len(points), points[0][1] + + for i in range(1, len(points)): + curr = points[i] + if curr[0] <= prevEnd: + res -= 1 + prevEnd = min(curr[1], prevEnd) + else: + prevEnd = curr[1] + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0])); + int res = points.length, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + int[] curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end()); + int res = points.size(), prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + vector& curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[0] - b[0]); + let res = points.length, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + let curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy (Sort By End Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort(key=lambda x: x[1]) + res, prevEnd = 1, points[0][1] + + for i in range(1, len(points)): + if points[i][0] > prevEnd: + prevEnd = points[i][1] + res += 1 + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1])); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[1] - b[1]); + let res = 1, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-number-of-days-to-eat-n-oranges.md b/articles/minimum-number-of-days-to-eat-n-oranges.md new file mode 100644 index 000000000..115da181e --- /dev/null +++ b/articles/minimum-number-of-days-to-eat-n-oranges.md @@ -0,0 +1,354 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {} + + def dfs(n): + if n == 0: + return 0 + if n in dp: + return dp[n] + + res = 1 + dfs(n - 1) + if n % 3 == 0: + res = min(res, 1 + dfs(n // 3)) + if n % 2 == 0: + res = min(res, 1 + dfs(n // 2)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + return dfs(n); + } + + private int dfs(int n) { + if (n == 0) return 0; + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + return dfs(n); + } + + int dfs(int n) { + if (n == 0) return 0; + if (dp.count(n)) return dp[n]; + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = min(res, 1 + dfs(n / 2)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + + const dfs = (n) => { + if (n === 0) return 0; + if (dp.has(n)) return dp.get(n); + + let res = 1 + dfs(n - 1); + if (n % 3 === 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 === 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {0: 0, 1: 1} + + def dfs(n): + if n in dp: + return dp[n] + + res = 1 + (n % 2) + dfs(n // 2) + res = min(res, 1 + (n % 3) + dfs(n // 3)) + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + dp.put(0, 0); + dp.put(1, 1); + return dfs(n); + } + + private int dfs(int n) { + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + (n % 2) + dfs(n / 2); + res = Math.min(res, 1 + (n % 3) + dfs(n / 3)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + dp[0] = 0; + dp[1] = 1; + return dfs(n); + } + +private: + int dfs(int n) { + if (dp.count(n)) return dp[n]; + + int res = 1 + (n % 2) + dfs(n / 2); + res = min(res, 1 + (n % 3) + dfs(n / 3)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + dp.set(0, 0); + dp.set(1, 1); + + const dfs = (n) => { + if (dp.has(n)) return dp.get(n); + + let res = 1 + (n % 2) + dfs(Math.floor(n / 2)); + res = Math.min(res, 1 + (n % 3) + dfs(Math.floor(n / 3))); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + q = deque([n]) + visit = set() + res = 0 + + while q: + res += 1 + for _ in range(len(q)): + node = q.popleft() + nei = node - 1 + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + for d in range(2, 4): + if node % d == 0: + nei = node // d + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + return res +``` + +```java +public class Solution { + public int minDays(int n) { + Queue q = new LinkedList<>(); + Set visit = new HashSet<>(); + q.offer(n); + int res = 0; + + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.poll(); + int nei = node - 1; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDays(int n) { + queue q; + unordered_set visit; + q.push(n); + int res = 0; + + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.front(); q.pop(); + int nei = node - 1; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const q = new Queue([n]); + const visit = new Set(); + let res = 0; + + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + let nei = node - 1; + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + for (let d = 2; d <= 3; d++) { + if (node % d === 0) { + nei = Math.floor(node / d); + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/minimum-number-of-vertices-to-reach-all-nodes.md b/articles/minimum-number-of-vertices-to-reach-all-nodes.md new file mode 100644 index 000000000..aa09f1c80 --- /dev/null +++ b/articles/minimum-number-of-vertices-to-reach-all-nodes.md @@ -0,0 +1,465 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = set(range(n)) + visited = [False] * n + + def dfs(node): + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + dfs(nei) + res.discard(nei) + + for i in range(n): + if not visited[i]: + dfs(i) + return list(res) +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + Set res = new HashSet<>(); + for (int i = 0; i < n; i++) { + res.add(i); + } + + boolean[] visited = new boolean[n]; + for (int i = 0; i < n; i++) { + dfs(i, adj, visited, res); + } + return new ArrayList<>(res); + } + + private void dfs(int node, List[] adj, boolean[] visited, Set res) { + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) dfs(nei, adj, visited, res); + res.remove(nei); + } + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + unordered_set res; + vector visited(n, false); + for (int i = 0; i < n; i++) res.insert(i); + + function dfs = [&](int node) { + visited[node] = true; + for (int& nei : adj[node]) { + if (!visited[nei]) dfs(nei); + res.erase(nei); + } + }; + + for (int i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = new Set(Array.from({ length: n }, (_, i) => i)); + const visited = new Array(n).fill(false); + + const dfs = (node) => { + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) dfs(nei); + res.delete(nei); + } + }; + + for (let i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = [True] * n + visited = [False] * n + stack = [] + + for i in range(n): + if not visited[i]: + stack.append(i) + while stack: + node = stack.pop() + if visited[node]: + continue + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + stack.append(nei) + res[nei] = False + + return [i for i in range(n) if res[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + boolean[] res = new boolean[n]; + Arrays.fill(res, true); + boolean[] visited = new boolean[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (res[i]) result.add(i); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + vector res(n, true), visited(n, false); + stack stack; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (res[i]) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = Array(n).fill(true); + const visited = Array(n).fill(false); + const stack = []; + + for (let i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (stack.length) { + const node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + return res.map((val, i) => val ? i : -1).filter(i => i !== -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + incoming = collections.defaultdict(list) + for src, dst in edges: + incoming[dst].append(src) + + res = [] + for i in range(n): + if not incoming[i]: + res.append(i) + return res +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] incoming = new ArrayList[n]; + for (int i = 0; i < n; i++) { + incoming[i] = new ArrayList<>(); + } + for (List edge : edges) { + incoming[edge.get(1)].add(edge.get(0)); + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (incoming[i].isEmpty()) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> incoming(n); + for (auto& edge : edges) { + incoming[edge[1]].push_back(edge[0]); + } + + vector res; + for (int i = 0; i < n; i++) { + if (incoming[i].empty()) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const incoming = Array.from({ length: n }, () => []); + + for (const [src, dst] of edges) { + incoming[dst].push(src); + } + + const res = []; + for (let i = 0; i < n; i++) { + if (incoming[i].length === 0) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + indegree = [False] * n + for src, dst in edges: + indegree[dst] = True + return [i for i in range(n) if not indegree[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + boolean[] indegree = new boolean[n]; + for (List edge : edges) { + indegree[edge.get(1)] = true; + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector indegree(n, false); + for (const auto& edge : edges) { + indegree[edge[1]] = true; + } + + vector res; + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const indegree = new Array(n).fill(false); + for (const [src, dst] of edges) { + indegree[dst] = true; + } + + let res = []; + for (let i = 0; i < n; i++) { + if (!indegree[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/minimum-one-bit-operations-to-make-integers-zero.md b/articles/minimum-one-bit-operations-to-make-integers-zero.md new file mode 100644 index 000000000..7e4071ce6 --- /dev/null +++ b/articles/minimum-one-bit-operations-to-make-integers-zero.md @@ -0,0 +1,313 @@ +## 1. Math (Recursion) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + if n == 0: + return 0 + + k = 1 + while (k << 1) <= n: + k <<= 1 + + return (k << 1) - 1 - self.minimumOneBitOperations(k ^ n) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + if (n === 0) { + return 0; + } + + let k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - this.minimumOneBitOperations(k ^ n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 2. Math (Iteration) - I + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = 0 + k = 1 << 30 + sign = 1 + + while n: + while k > n: + k >>= 1 + + res += (sign * ((k << 1) - 1)) + sign *= -1 + n ^= k + + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += (sign * ((k << 1) - 1)); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, k = 1 << 30, sign = 1; + + while (n !== 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (Iteration) - II + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res, sign = 0, 1 + while n: + res += sign * (n ^ (n - 1)) + n &= (n - 1) + sign *= -1 + return abs(res) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return abs(res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, sign = 1; + while (n !== 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Math (Grey Code) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = n + while n: + n >>= 1 + res ^= n + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = n; + while (n !== 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-good-paths.md b/articles/number-of-good-paths.md new file mode 100644 index 000000000..f0e16529f --- /dev/null +++ b/articles/number-of-good-paths.md @@ -0,0 +1,861 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, startNode, parent): + if vals[node] > vals[startNode]: + return 0 + + res = 0 + if vals[node] == vals[startNode] and node >= startNode: + res += 1 + + for child in adj[node]: + if child == parent: + continue + res += dfs(child, startNode, node) + + return res + + + res = 0 + for node in range(n): + res += dfs(node, node, -1) + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + + private int dfs(int node, int startNode, int parent, int[] vals, List[] adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + +private: + int dfs(int node, int startNode, int parent, vector& vals, vector>& adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, startNode, parent) => { + if (vals[node] > vals[startNode]) { + return 0; + } + + let res = 0; + if (vals[node] === vals[startNode] && node >= startNode) { + res += 1; + } + + for (const child of adj[node]) { + if (child === parent) continue; + res += dfs(child, startNode, node); + } + return res; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res += dfs(node, node, -1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Brute Force (BFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals, edges): + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + res = 0 + for startNode in range(n): + q = deque([startNode]) + visited = set([startNode]) + count = 0 + + while q: + node = q.popleft() + if vals[node] == vals[startNode] and node >= startNode: + count += 1 + + for child in adj[node]: + if child not in visited and vals[child] <= vals[startNode]: + visited.add(child) + q.append(child) + + res += count + + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(startNode); + visited.add(startNode); + int count = 0; + + while (!q.isEmpty()) { + int node = q.poll(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (!visited.contains(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.offer(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + queue q; + unordered_set visited; + q.push(startNode); + visited.insert(startNode); + int count = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (visited.find(child) == visited.end() && vals[child] <= vals[startNode]) { + visited.insert(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + let res = 0; + for (let startNode = 0; startNode < n; startNode++) { + const q = new Queue([startNode]); + const visited = new Set([startNode]); + let count = 0; + + while (q.length) { + let node = q.shift(); + if (vals[node] === vals[startNode] && node >= startNode) { + count++; + } + + for (const child of adj[node]) { + if (!visited.has(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + adj = collections.defaultdict(list) + for a, b in edges: + adj[a].append(b) + adj[b].append(a) + + valToIndex = collections.defaultdict(list) + for i, val in enumerate(vals): + valToIndex[val].append(i) + + res = 0 + uf = DSU(len(vals)) + + for val in sorted(valToIndex.keys()): + for i in valToIndex[val]: + for nei in adj[i]: + if vals[nei] <= vals[i]: + uf.union(nei, i) + + count = collections.defaultdict(int) + for i in valToIndex[val]: + count[uf.find(i)] += 1 + res += count[uf.find(i)] + + return res +``` + +```java +class DSU { + private int[] parent, size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + TreeMap> valToIndex = new TreeMap<>(); + for (int i = 0; i < n; i++) { + valToIndex.putIfAbsent(vals[i], new ArrayList<>()); + valToIndex.get(vals[i]).add(i); + } + + DSU dsu = new DSU(n); + int res = 0; + + for (int val : valToIndex.keySet()) { + for (int i : valToIndex.get(val)) { + for (int nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + Map count = new HashMap<>(); + for (int i : valToIndex.get(val)) { + int root = dsu.find(i); + count.put(root, count.getOrDefault(root, 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, size; + + DSU(int n) { + parent.resize(n + 1); + size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) parent[node] = find(parent[node]); + return parent[node]; + } + + bool unite(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + map> valToIndex; + for (int i = 0; i < n; i++) valToIndex[vals[i]].push_back(i); + + DSU dsu(n); + int res = 0; + + for (auto& [val, nodes] : valToIndex) { + for (int& i : nodes) { + for (int& nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.unite(nei, i); + } + } + + unordered_map count; + for (int& i : nodes) { + int root = dsu.find(i); + count[root]++; + res += count[root]; + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const valToIndex = new Map(); + for (let i = 0; i < n; i++) { + if (!valToIndex.has(vals[i])) valToIndex.set(vals[i], []); + valToIndex.get(vals[i]).push(i); + } + + const dsu = new DSU(n); + let res = 0; + + for (const [val, nodes] of [...valToIndex.entries()].sort((a, b) => a[0] - b[0])) { + for (const i of nodes) { + for (const nei of adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + const count = new Map(); + for (const i of nodes) { + const root = dsu.find(i); + count.set(root, (count.get(root) || 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union (Union By Value) + +::tabs-start + +```python +class DSU: + def __init__(self, n, vals): + self.parent = list(range(n)) + self.vals = vals + self.count = [1] * n # count of nodes with max value of the component + + def find(self, node): + if self.parent[node] != node: + self.parent[node] = self.find(self.parent[node]) + return self.parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return 0 + if self.vals[pu] < self.vals[pv]: + self.parent[pu] = pv + elif self.vals[pu] > self.vals[pv]: + self.parent[pv] = pu + else: + self.parent[pv] = pu + result = self.count[pu] * self.count[pv] + self.count[pu] += self.count[pv] + return result + + return 0 + + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + dsu = DSU(n, vals) + + # Sort edges based on max value of the two nodes + edges.sort(key=lambda edge: max(vals[edge[0]], vals[edge[1]])) + + res = n # Each node alone is a good path + for u, v in edges: + res += dsu.union(u, v) + return res +``` + +```java +class DSU { + private int[] parent, count, vals; + + public DSU(int n, int[] vals) { + this.parent = new int[n]; + this.vals = vals; + this.count = new int[n]; // count of nodes with max value of the component + Arrays.fill(count, 1); + + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + DSU dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + Arrays.sort(edges, + Comparator.comparingInt(edge -> Math.max(vals[edge[0]], vals[edge[1]])) + ); + + int res = n; // Each node alone is a good path + for (int[] edge : edges) { + res += dsu.union(edge[0], edge[1]); + } + return res; + } +} +``` + +```cpp +class DSU { + vector parent, count, vals; + +public: + DSU(int n, vector& vals) : vals(vals), parent(n), count(n, 1) { + for (int i = 0; i < n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionNodes(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + DSU dsu(n, vals); + + // Sort edges based on max value of the two nodes + sort(edges.begin(), edges.end(), [&](auto& a, auto& b) { + return max(vals[a[0]], vals[a[1]]) < max(vals[b[0]], vals[b[1]]); + }); + + int res = n; // Each node alone is a good path + for (auto& edge : edges) { + res += dsu.unionNodes(edge[0], edge[1]); + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + * @param {number[]} vals + */ + constructor(n, vals) { + this.parent = Array(n).fill(0).map((_, i) => i); + this.vals = vals; + this.count = Array(n).fill(1); // count of nodes with max value of the component + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.vals[pu] < this.vals[pv]) { + this.parent[pu] = pv; + } else if (this.vals[pu] > this.vals[pv]) { + this.parent[pv] = pu; + } else { + this.parent[pv] = pu; + let result = this.count[pu] * this.count[pv]; + this.count[pu] += this.count[pv]; + return result; + } + return 0; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + let n = vals.length; + let dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + edges.sort((a, b) => + Math.max(vals[a[0]], vals[a[1]]) - Math.max(vals[b[0]], vals[b[1]]) + ); + + let res = n; // Each node alone is a good path + for (let [u, v] of edges) { + res += dsu.union(u, v); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-laser-beams-in-a-bank.md b/articles/number-of-laser-beams-in-a-bank.md new file mode 100644 index 000000000..598d7fbb2 --- /dev/null +++ b/articles/number-of-laser-beams-in-a-bank.md @@ -0,0 +1,110 @@ +## 1. Counting + +::tabs-start + +```python +class Solution: + def numberOfBeams(self, bank: List[str]) -> int: + prev = bank[0].count("1") + res = 0 + + for i in range(1, len(bank)): + curr = bank[i].count("1") + if curr: + res += prev * curr + prev = curr + + return res +``` + +```java +public class Solution { + public int numberOfBeams(String[] bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.length; i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + + private int countOnes(String s) { + int count = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '1') count++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int numberOfBeams(vector& bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.size(); i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + +private: + int countOnes(const string& s) { + return count(s.begin(), s.end(), '1'); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} bank + * @return {number} + */ + numberOfBeams(bank) { + const countOnes = (s) => { + let cnt = 0; + for (let c of s) { + if (c === '1') cnt += 1; + } + return cnt; + }; + + let prev = countOnes(bank[0]); + let res = 0; + + for (let i = 1; i < bank.length; i++) { + let curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/number-of-ways-to-divide-a-long-corridor.md b/articles/number-of-ways-to-divide-a-long-corridor.md new file mode 100644 index 000000000..cda191a27 --- /dev/null +++ b/articles/number-of-ways-to-divide-a-long-corridor.md @@ -0,0 +1,608 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + cache = [[-1] * 3 for i in range(len(corridor))] # (i, seats) -> count + + def dfs(i, seats): + if i == len(corridor): + return 1 if seats == 2 else 0 + if cache[i][seats] != -1: + return cache[i][seats] + + res = 0 + if seats == 2: + if corridor[i] == "S": + res = dfs(i + 1, 1) + else: + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % mod + else: + if corridor[i] == "S": + res = dfs(i + 1, seats + 1) + else: + res = dfs(i + 1, seats) + + cache[i][seats] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numberOfWays(String corridor) { + int n = corridor.length(); + dp = new int[n][3]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + return dfs(0, 0, corridor); + } + + private int dfs(int i, int seats, String corridor) { + if (i == corridor.length()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +} +``` + +```cpp +class Solution { +public: + static constexpr int MOD = 1'000'000'007; + vector> dp; + + int numberOfWays(string corridor) { + int n = corridor.size(); + dp.assign(n, vector(3, -1)); + return dfs(0, 0, corridor); + } + + int dfs(int i, int seats, string& corridor) { + if (i == corridor.size()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor[i] == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor[i] == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1_000_000_007; + const n = corridor.length; + const dp = Array.from({ length: n }, () => Array(3).fill(-1)); + + const dfs = (i, seats) => { + if (i === n) return seats === 2 ? 1 : 0; + if (dp[i][seats] !== -1) return dp[i][seats]; + + let res = 0; + if (seats === 2) { + if (corridor[i] === 'S') { + res = dfs(i + 1, 1); + } else { + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % MOD; + } + } else { + if (corridor[i] === 'S') { + res = dfs(i + 1, seats + 1); + } else { + res = dfs(i + 1, seats); + } + } + + return (dp[i][seats] = res); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + n = len(corridor) + dp = [[0] * 3 for _ in range(n + 1)] + dp[n][2] = 1 + + for i in range(n - 1, -1, -1): + for seats in range(3): + if seats == 2: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][1] + else: + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD + else: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][seats + 1] + else: + dp[i][seats] = dp[i + 1][seats] + + return dp[0][0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int n = corridor.length(); + int[][] dp = new int[n + 1][3]; + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int MOD = 1000000007; + int n = corridor.size(); + vector> dp(n + 1, vector(3, 0)); + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + const n = corridor.length; + let dp = Array.from({ length: n + 1 }, () => Array(3).fill(0)); + dp[n][2] = 1; + + for (let i = n - 1; i >= 0; i--) { + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + dp = [0, 0, 1] + + for i in reversed(corridor): + new_dp = [0] * 3 + for seats in range(3): + if seats == 2: + new_dp[seats] = dp[1] if i == 'S' else (dp[0] + dp[2]) % MOD + else: + new_dp[seats] = dp[seats + 1] if i == 'S' else dp[seats] + dp = new_dp + + return dp[0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int[] dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + int[] new_dp = new int[3]; + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + const int MOD = 1000000007; + vector dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + vector new_dp(3, 0); + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = (corridor[i] == 'S') ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = (corridor[i] == 'S') ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + let dp = [0, 0, 1]; + + for (let i = corridor.length - 1; i >= 0; i--) { + let new_dp = [0, 0, 0]; + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + new_dp[seats] = corridor[i] === 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor[i] === 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Combinatorics + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + seats = [i for i, c in enumerate(corridor) if c == "S"] + + length = len(seats) + if length < 2 or length % 2 == 1: + return 0 + + res = 1 + for i in range(1, length - 1, 2): + res = (res * (seats[i + 1] - seats[i])) % mod + + return res +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + List seats = new ArrayList<>(); + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + seats.add(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats.get(i + 1) - seats.get(i))) % mod; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007; + vector seats; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + seats.push_back(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + const seats = []; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + seats.push(i); + } + } + + const length = seats.length; + if (length < 2 || length % 2 === 1) { + return 0; + } + + let res = 1; + for (let i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics (Optimal) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 1_000_000_007 + count = 0 + res = 1 + prev = -1 + + for i, c in enumerate(corridor): + if c == 'S': + count += 1 + if count > 2 and count % 2 == 1: + res = (res * (i - prev)) % mod + prev = i + + return res if count >= 2 and count % 2 == 0 else 0 +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + int count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (int)((res * (long)(i - prev)) % mod); + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007, count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (1LL * res * (i - prev)) % mod; + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + let count = 0, res = 1, prev = -1; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + count++; + if (count > 2 && count % 2 === 1) { + res = (res * (i - prev)) % mod; + } + prev = i; + } + } + + return count >= 2 && count % 2 === 0 ? res : 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/operations-on-tree.md b/articles/operations-on-tree.md new file mode 100644 index 000000000..682f18874 --- /dev/null +++ b/articles/operations-on-tree.md @@ -0,0 +1,806 @@ +## 1. Depth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + def dfs(node): + lockedCount = 0 + if self.locked[node]: + lockedCount += 1 + self.locked[node] = 0 + + for nei in self.child[node]: + lockedCount += dfs(nei) + return lockedCount + + if dfs(num) > 0: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + + private int dfs(int node) { + int lockedCount = 0; + if (locked[node] != 0) { + lockedCount++; + locked[node] = 0; + } + for (int nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + +private: + int dfs(int node) { + int lockedCount = 0; + if (locked[node]) { + lockedCount++; + locked[node] = 0; + } + for (int& nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + const dfs = (node) => { + let lockedCount = 0; + if (this.locked[node] !== 0) { + lockedCount++; + this.locked[node] = 0; + } + for (let nei of this.child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + }; + + if (dfs(num) > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, q = 0, deque([num]) + while q: + node = q.popleft() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + q.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Queue q = new LinkedList<>(); + q.offer(num); + + while (!q.isEmpty()) { + node = q.poll(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + q.addAll(child[node]); + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + queue q; + q.push(num); + + while (!q.empty()) { + node = q.front(); q.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + const q = new Queue([num]); + + while (!q.isEmpty()) { + node = q.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + for (let nei of this.child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, stack = 0, [num] + while stack: + node = stack.pop() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + stack.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Stack stack = new Stack<>(); + stack.push(num); + + while (!stack.isEmpty()) { + node = stack.pop(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + stack.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + stack stk; + stk.push(num); + + while (!stk.empty()) { + node = stk.top(); stk.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int& nei : child[node]) { + stk.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + let stack = [num]; + + while (stack.length) { + node = stack.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + stack.push(...this.child[node]); + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/parallel-courses-iii.md b/articles/parallel-courses-iii.md new file mode 100644 index 000000000..ca66a0769 --- /dev/null +++ b/articles/parallel-courses-iii.md @@ -0,0 +1,481 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = defaultdict(list) + for src, dst in relations: + adj[src].append(dst) + + max_time = {} + + def dfs(src): + if src in max_time: + return max_time[src] + res = time[src - 1] + for nei in adj[src]: + res = max(res, time[src - 1] + dfs(nei)) + max_time[src] = res + return res + + for i in range(1, n + 1): + dfs(i) + + return max(max_time.values()) +``` + +```java +public class Solution { + private Map maxTime; + private List[] adj; + private int[] time; + + public int minimumTime(int n, int[][] relations, int[] time) { + this.time = time; + this.maxTime = new HashMap<>(); + this.adj = new ArrayList[n + 1]; + + for (int i = 1; i <= n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] relation : relations) { + adj[relation[0]].add(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + return Collections.max(maxTime.values()); + } + + private int dfs(int src) { + if (maxTime.containsKey(src)) { + return maxTime.get(src); + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.put(src, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map maxTime; + vector> adj; + vector time; + + int minimumTime(int n, vector>& relations, vector& time) { + this->time = time; + adj.resize(n + 1); + + for (auto& relation : relations) { + adj[relation[0]].push_back(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + int res = 0; + for (auto& [key, value] : maxTime) { + res = max(res, value); + } + return res; + } + +private: + int dfs(int src) { + if (maxTime.count(src)) { + return maxTime[src]; + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = max(res, time[src - 1] + dfs(nei)); + } + maxTime[src] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n + 1 }, () => []); + let maxTime = new Map(); + + for (let [src, dst] of relations) { + adj[src].push(dst); + } + + const dfs = (src) => { + if (maxTime.has(src)) { + return maxTime.get(src); + } + + let res = time[src - 1]; + for (let nei of adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.set(src, res); + return res; + }; + + for (let i = 1; i <= n; i++) { + dfs(i); + } + + return Math.max(...maxTime.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + for src, dst in relations: + adj[src - 1].append(dst - 1) + + maxTime = [-1] * n + processed = [False] * n + + for i in range(n): + if maxTime[i] == -1: + stack = [i] + while stack: + node = stack.pop() + if processed[node]: + best = 0 + for nei in adj[node]: + best = max(best, maxTime[nei]) + maxTime[node] = time[node] + best + else: + processed[node] = True + stack.append(node) + for nei in adj[node]: + if maxTime[nei] == -1: + stack.append(nei) + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] rel : relations) { + adj[rel[0] - 1].add(rel[1] - 1); + } + + int[] maxTime = new int[n]; + Arrays.fill(maxTime, -1); + boolean[] processed = new boolean[n]; + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + Stack stack = new Stack<>(); + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stack.push(nei); + } + } + } + } + } + } + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + for (auto& rel : relations) { + adj[rel[0] - 1].push_back(rel[1] - 1); + } + + vector maxTime(n, -1); + vector processed(n, false); + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + stack stk; + stk.push(i); + while (!stk.empty()) { + int node = stk.top(); stk.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stk.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stk.push(nei); + } + } + } + } + } + } + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + } + + let maxTime = Array(n).fill(-1); + let processed = Array(n).fill(false); + + for (let i = 0; i < n; i++) { + if (maxTime[i] === -1) { + let stack = [i]; + while (stack.length > 0) { + let node = stack.pop(); + if (processed[node]) { + let best = 0; + for (let nei of adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (let nei of adj[node]) { + if (maxTime[nei] === -1) { + stack.push(nei); + } + } + } + } + } + } + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + indegree = [0] * n + maxTime = time[:] + + for src, dst in relations: + adj[src - 1].append(dst - 1) + indegree[dst - 1] += 1 + + queue = deque([i for i in range(n) if indegree[i] == 0]) + while queue: + node = queue.popleft() + for nei in adj[node]: + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]) + indegree[nei] -= 1 + if indegree[nei] == 0: + queue.append(nei) + + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List> adj = new ArrayList<>(); + int[] indegree = new int[n]; + int[] maxTime = Arrays.copyOf(time, n); + + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj.get(src).add(dst); + indegree[dst]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.get(node)) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.add(nei); + } + } + } + + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + vector indegree(n, 0); + vector maxTime(time.begin(), time.end()); + + for (auto& relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj[src].push_back(dst); + indegree[dst]++; + } + + queue queue; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); queue.pop(); + for (int nei : adj[node]) { + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.push(nei); + } + } + } + + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + let indegree = Array(n).fill(0); + let maxTime = [...time]; + + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + indegree[dst - 1]++; + } + + let queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + let node = queue.pop(); + for (let nei of adj[node]) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/partition-array-for-maximum-sum.md b/articles/partition-array-for-maximum-sum.md new file mode 100644 index 000000000..a2fb52393 --- /dev/null +++ b/articles/partition-array-for-maximum-sum.md @@ -0,0 +1,446 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + def dfs(i): + if i >= len(arr): + return 0 + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + return dfs(0, arr, k); + } + + private int dfs(int i, int[] arr, int k) { + if (i >= arr.length) { + return 0; + } + + int cur_max = 0; + int res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + return dfs(0, arr, k); + } + +private: + int dfs(int i, vector& arr, int k) { + if (i >= arr.size()) { + return 0; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const dfs = (i) => { + if (i >= arr.length) { + return 0; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + cache = { len(arr) : 0 } + + def dfs(i): + if i in cache: + return cache[i] + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + cache[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int[] cache = new int[arr.length + 1]; + Arrays.fill(cache, -1); + cache[arr.length] = 0; + return dfs(0, arr, k, cache); + } + + private int dfs(int i, int[] arr, int k, int[] cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + cache[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + vector cache(arr.size() + 1, -1); + cache[arr.size()] = 0; + return dfs(0, arr, k, cache); + } + +private: + int dfs(int i, vector& arr, int k, vector& cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const cache = new Array(arr.length + 1).fill(-1); + cache[arr.length] = 0; + + const dfs = (i) => { + if (cache[i] !== -1) { + return cache[i]; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return (cache[i] = res); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + n = len(arr) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + cur_max = 0 + for j in range(i, min(n, i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size) + + return dp[0] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < min(n, i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + let n = arr.length; + let dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let cur_max = 0; + for (let j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + dp = [0] * k + dp[0] = arr[0] + + for i in range(1, len(arr)): + cur_max = 0 + max_at_i = 0 + for j in range(i, i - k, -1): + if j < 0: + break + cur_max = max(cur_max, arr[j]) + window_size = i - j + 1 + cur_sum = cur_max * window_size + sub_sum = dp[(j - 1) % k] if j > 0 else 0 + max_at_i = max(max_at_i, cur_sum + sub_sum) + + dp[i % k] = max_at_i + + return dp[(len(arr) - 1) % k] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[k]; + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(k); + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const n = arr.length; + const dp = new Array(k).fill(0); + dp[0] = arr[0]; + + for (let i = 1; i < n; i++) { + let cur_max = 0, max_at_i = 0; + for (let j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + let window_size = i - j + 1; + let cur_sum = cur_max * window_size; + let sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. \ No newline at end of file diff --git a/articles/path-with-maximum-gold.md b/articles/path-with-maximum-gold.md new file mode 100644 index 000000000..81c3d201d --- /dev/null +++ b/articles/path-with-maximum-gold.md @@ -0,0 +1,508 @@ +## 1. Backtracking (DFS) - I + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c, visit): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0 or (r, c) in visit: + return 0 + + visit.add((r, c)) + res = grid[r][c] + + for dr, dc in directions: + res = max(res, grid[r][c] + dfs(r + dr, c + dc, visit)) + + visit.remove((r, c)) + return res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c, set())) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c, new boolean[ROWS][COLS])); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c, boolean[][] visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (int[] d : directions) { + res = Math.max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + vector> visit(ROWS, vector(COLS, false)); + res = max(res, dfs(grid, r, c, visit)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c, vector>& visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (auto& d : directions) { + res = max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c, visit) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + let res = grid[r][c]; + + for (const [dr, dc] of directions) { + res = Math.max(res, grid[r][c] + dfs(r + dr, c + dc, visit)); + } + + visit[r][c] = false; + return res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + let visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + res = Math.max(res, dfs(r, c, visit)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. + +--- + +## 2. Backtracking (DFS) - II + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0: + return 0 + + gold = grid[r][c] + grid[r][c] = 0 + res = 0 + + for dr, dc in directions: + res = max(res, dfs(r + dr, c + dc)) + + grid[r][c] = gold + return gold + res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c)) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c)); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (int[] d : directions) { + res = Math.max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = max(res, dfs(grid, r, c)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (auto& d : directions) { + res = max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0) { + return 0; + } + + let gold = grid[r][c]; + grid[r][c] = 0; + let res = 0; + + for (const [dr, dc] of directions) { + res = Math.max(res, dfs(r + dr, c + dc)); + } + + grid[r][c] = gold; + return gold + res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + res = Math.max(res, dfs(r, c)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the number of cells which contain gold. + +--- + +## 3. Backtracking (BFS) + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [1, 0, -1, 0, 1] + index = [[0] * COLS for _ in range(ROWS)] + idx = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + index[r][c] = idx + idx += 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] > 0: + q = deque([(r, c, grid[r][c], 1 << index[r][c])]) + while q: + row, col, gold, mask = q.popleft() + res = max(res, gold) + + for i in range(4): + nr, nc = row + directions[i], col + directions[i + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS and grid[nr][nc] > 0: + idx = index[nr][nc] + if not (mask & (1 << idx)): + q.append((nr, nc, gold + grid[nr][nc], mask | (1 << idx))) + + return res +``` + +```java +public class Solution { + public int getMaximumGold(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] index = new int[ROWS][COLS]; + int idx = 0; + int[] directions = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + Queue q = new LinkedList<>(); + q.offer(new int[]{r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int row = cur[0], col = cur[1], gold = cur[2], mask = cur[3]; + res = Math.max(res, gold); + + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.offer(new int[]{nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int getMaximumGold(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> index(ROWS, vector(COLS, 0)); + int idx = 0; + int directions[] = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + queue> q; + q.push({r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.empty()) { + auto [row, col, gold, mask] = q.front();q.pop(); + res = max(res, gold); + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.push({nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const index = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + let idx = 0; + const directions = [1, 0, -1, 0, 1]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + index[r][c] = idx++; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + const q = new Queue([[r, c, grid[r][c], 1 << index[r][c]]]); + + while (!q.isEmpty()) { + const [row, col, gold, mask] = q.pop(); + res = Math.max(res, gold); + for (let i = 0; i < 4; i++) { + const nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + const newIdx = index[nr][nc]; + if (!(mask & (1 << newIdx))) { + q.push([nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)]); + } + } + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. \ No newline at end of file diff --git a/articles/path-with-maximum-probability.md b/articles/path-with-maximum-probability.md new file mode 100644 index 000000000..b16d1e43b --- /dev/null +++ b/articles/path-with-maximum-probability.md @@ -0,0 +1,632 @@ +## 1. Dijkstra's Algorithm - I + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = collections.defaultdict(list) + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + pq = [(-1, start_node)] + visit = set() + + while pq: + prob, cur = heapq.heappop(pq) + visit.add(cur) + + if cur == end_node: + return -prob + + for nei, edgeProb in adj[cur]: + if nei not in visit: + heapq.heappush(pq, (prob * edgeProb, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = new Map(); + for (let i = 0; i < n; i++) adj.set(i, []); + + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj.get(src).push([dst, succProb[i]]); + adj.get(dst).push([src, succProb[i]]); + } + + let pq = new MaxPriorityQueue({ priority: x => x[0] }); + pq.enqueue([1.0, start_node]); + let visited = new Set(); + + while (!pq.isEmpty()) { + let [prob, cur] = pq.dequeue().element; + visited.add(cur); + + if (cur === end_node) return prob; + + for (let [nei, edgeProb] of adj.get(cur)) { + if (!visited.has(nei)) { + pq.enqueue([prob * edgeProb, nei]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 2. Dijkstra's Algorithm - II + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0] * n + maxProb[start_node] = 1.0 + pq = [(-1.0, start_node)] + + while pq: + curr_prob, node = heapq.heappop(pq) + curr_prob *= -1 + + if node == end_node: + return curr_prob + if curr_prob > maxProb[node]: + continue + + for nei, edge_prob in adj[node]: + new_prob = curr_prob * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + heapq.heappush(pq, (-new_prob, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = Array(n).fill(0); + maxProb[start_node] = 1.0; + let pq = new MaxPriorityQueue({ priority: x => x[1] }); + pq.enqueue([start_node, 1.0]); + + while (!pq.isEmpty()) { + let [node, curr_prob] = pq.dequeue().element; + + if (node === end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (let [nei, edge_prob] of adj[node]) { + let new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.enqueue([nei, new_prob]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 3. Bellman Ford Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + + for i in range(n): + updated = False + for j in range(len(edges)): + src, dst = edges[j] + if maxProb[src] * succProb[j] > maxProb[dst]: + maxProb[dst] = maxProb[src] * succProb[j] + updated = True + + if maxProb[dst] * succProb[j] > maxProb[src]: + maxProb[src] = maxProb[dst] * succProb[j] + updated = True + + if not updated: + break + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + boolean updated = false; + for (int j = 0; j < edges.length; j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + bool updated = false; + for (int j = 0; j < edges.size(); j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + + for (let i = 0; i < n; i++) { + let updated = false; + for (let j = 0; j < edges.length; j++) { + let [src, dst] = edges[j]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + q = deque([start_node]) + + while q: + node = q.popleft() + + for nei, edge_prob in adj[node]: + new_prob = maxProb[node] * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + q.append(nei) + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + Queue q = new LinkedList<>(); + q.offer(start_node); + + while (!q.isEmpty()) { + int node = q.poll(); + + for (Pair nei : adj[node]) { + double newProb = maxProb[node] * nei.prob; + if (newProb > maxProb[nei.node]) { + maxProb[nei.node] = newProb; + q.offer(nei.node); + } + } + } + + return maxProb[end_node]; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + queue q; + q.push(start_node); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + + for (auto& [nei, edgeProb] : adj[node]) { + double newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + const q = new Queue([start_node]); + + while (!q.isEmpty()) { + let node = q.pop(); + + for (let [nei, edgeProb] of adj[node]) { + let newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md new file mode 100644 index 000000000..156cbb636 --- /dev/null +++ b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md @@ -0,0 +1,322 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + s = list(colors) + + def removeChar(c): + for i in range(1, len(s) - 1): + if s[i] != c: + continue + + if s[i] == s[i + 1] == s[i - 1]: + s.pop(i) + return True + return False + + while True: + if not removeChar('A'): + return False + if not removeChar('B'): + return True + + return False +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + StringBuilder s = new StringBuilder(colors); + + while (true) { + if (!removeChar(s, 'A')) return false; + if (!removeChar(s, 'B')) return true; + } + } + + private boolean removeChar(StringBuilder s, char c) { + for (int i = 1; i < s.length() - 1; i++) { + if (s.charAt(i) != c) continue; + + if (s.charAt(i - 1) == c && s.charAt(i + 1) == c) { + s.deleteCharAt(i); + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + while (true) { + if (!removeChar(colors, 'A')) return false; + if (!removeChar(colors, 'B')) return true; + } + } + +private: + bool removeChar(string& s, char c) { + for (int i = 1; i < s.size() - 1; i++) { + if (s[i] != c) continue; + + if (s[i - 1] == c && s[i + 1] == c) { + s.erase(i, 1); + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let s = colors.split(""); + + const removeChar = (c) => { + for (let i = 1; i < s.length - 1; i++) { + if (s[i] !== c) continue; + + if (s[i - 1] === c && s[i + 1] === c) { + s.splice(i, 1); + return true; + } + } + return false; + }; + + while (true) { + if (!removeChar('A')) return false; + if (!removeChar('B')) return true; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice = bob = l = 0 + + for r in range(len(colors)): + if colors[l] != colors[r]: + l = r + + extra = r - l - 1 + if extra > 0: + if colors[l] == 'A': + alice += 1 + else: + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.length(); r++) { + if (colors.charAt(l) != colors.charAt(r)) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors.charAt(l) == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.size(); r++) { + if (colors[l] != colors[r]) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors[l] == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0, l = 0; + + for (let r = 0; r < colors.length; r++) { + if (colors[l] !== colors[r]) { + l = r; + } + + let extra = r - l - 1; + if (extra > 0) { + if (colors[l] === 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice, bob = 0, 0 + + for i in range(1, len(colors) - 1): + if colors[i - 1] == colors[i] == colors[i + 1]: + if colors[i] == 'A': + alice += 1 + if colors[i] == 'B': + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.length() - 1; i++) { + if (colors.charAt(i - 1) == colors.charAt(i) && + colors.charAt(i) == colors.charAt(i + 1)) { + if (colors.charAt(i) == 'A') { + alice++; + } + if (colors.charAt(i) == 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.size() - 1; i++) { + if (colors[i - 1] == colors[i] && colors[i] == colors[i + 1]) { + if (colors[i] == 'A') { + alice++; + } + if (colors[i] == 'B') { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0; + + for (let i = 1; i < colors.length - 1; i++) { + if (colors[i - 1] === colors[i] && colors[i] === colors[i + 1]) { + if (colors[i] === 'A') { + alice++; + } + if (colors[i] === 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-covered-intervals.md b/articles/remove-covered-intervals.md new file mode 100644 index 000000000..f71a0cfcc --- /dev/null +++ b/articles/remove-covered-intervals.md @@ -0,0 +1,275 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + n = len(intervals) + res = n + + for i in range(n): + for j in range(n): + if (i != j and intervals[j][0] <= intervals[i][0] and + intervals[j][1] >= intervals[i][1] + ): + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + int n = intervals.length; + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + int n = intervals.size(); + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + let n = intervals.length; + let res = n; + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i !== j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting - I + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key=lambda x: (x[0], -x[1])) + res = 1 + prevL, prevR = intervals[0][0], intervals[0][1] + for l, r in intervals: + if prevL <= l and prevR >= r: + continue + res += 1 + prevL, prevR = l, r + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> + a[0] == b[0] ? Integer.compare(b[1], a[1]) : Integer.compare(a[0], b[0]) + ); + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) { + return a[0] == b[0] ? b[1] < a[1] : a[0] < b[0]; + }); + + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + let res = 1, [prevL, prevR] = intervals[0]; + for (const [l, r] of intervals) { + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Sorting - II + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort() + res, start, end = 1, intervals[0][0], intervals[0][1] + + for l, r in intervals: + if start < l and end < r: + start = l + res += 1 + end = max(end, r) + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = max(end, r); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + let res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const [l, r] of intervals) { + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md new file mode 100644 index 000000000..04c3da1ac --- /dev/null +++ b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md @@ -0,0 +1,274 @@ +## 1. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return 0 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + self.n -= 1 + return 1 + + def isConnected(self): + return self.n == 1 + +class Solution: + def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int: + alice, bob = DSU(n), DSU(n) + cnt = 0 + + for type, src, dst in edges: + if type == 3: + cnt += (alice.union(src, dst) | bob.union(src, dst)) + + for type, src, dst in edges: + if type == 1: + cnt += alice.union(src, dst) + elif type == 2: + cnt += bob.union(src, dst) + + if alice.isConnected() and bob.isConnected(): + return len(edges) - cnt + return -1 +``` + +```java +class DSU { + private int[] parent, size; + private int n; + + public DSU(int n) { + this.n = n; + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public int maxNumEdgesToRemove(int n, int[][] edges) { + DSU alice = new DSU(n), bob = new DSU(n); + int cnt = 0; + + for (int[] edge : edges) { + if (edge[0] == 3) { + cnt += (alice.union(edge[1], edge[2]) | bob.union(edge[1], edge[2])); + } + } + + for (int[] edge : edges) { + if (edge[0] == 1) { + cnt += alice.union(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.union(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.length - cnt; + } + return -1; + } +} +``` + +```cpp +class DSU { +private: + vector parent, size; + int n; + +public: + DSU(int n) : n(n), parent(n + 1), size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + swap(pu, pv); + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + int maxNumEdgesToRemove(int n, vector>& edges) { + DSU alice(n), bob(n); + int cnt = 0; + + for (auto& edge : edges) { + if (edge[0] == 3) { + cnt += (alice.unionSets(edge[1], edge[2]) | bob.unionSets(edge[1], edge[2])); + } + } + + for (auto& edge : edges) { + if (edge[0] == 1) { + cnt += alice.unionSets(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.unionSets(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.size() - cnt; + } + return -1; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + */ + constructor(n) { + this.n = n; + this.parent = Array(n + 1).fill(0).map((_, i) => i); + this.size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.size[pu] < this.size[pv]) { + [pu, pv] = [pv, pu]; + } + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + this.n--; + return 1; + } + + /** + * @return {boolean} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number} + */ + maxNumEdgesToRemove(n, edges) { + let alice = new DSU(n), bob = new DSU(n); + let cnt = 0; + + for (let [type, src, dst] of edges) { + if (type === 3) { + cnt += (alice.union(src, dst) | bob.union(src, dst)); + } + } + + for (let [type, src, dst] of edges) { + if (type === 1) { + cnt += alice.union(src, dst); + } else if (type === 2) { + cnt += bob.union(src, dst); + } + } + + return alice.isConnected() && bob.isConnected() ? edges.length - cnt : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E * α(V))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/reveal-cards-in-increasing-order.md b/articles/reveal-cards-in-increasing-order.md new file mode 100644 index 000000000..4759af658 --- /dev/null +++ b/articles/reveal-cards-in-increasing-order.md @@ -0,0 +1,406 @@ +## 1. Simulation Using Queue - I + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + res = [0] * len(deck) + q = deque(range(len(deck))) + + for num in deck: + i = q.popleft() + res[i] = num + if q: + q.append(q.popleft()) + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + int n = deck.length; + int[] res = new int[n]; + Queue q = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + q.offer(i); + } + + for (int num : deck) { + int i = q.poll(); + res[i] = num; + if (!q.isEmpty()) { + q.offer(q.poll()); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + int n = deck.size(); + vector res(n); + queue q; + + for (int i = 0; i < n; i++) { + q.push(i); + } + + for (int num : deck) { + int i = q.front(); + q.pop(); + res[i] = num; + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + let n = deck.length; + let res = new Array(n).fill(0); + const q = new Queue(); + + for (let i = 0; i < n; i++) { + q.push(i); + } + + for (let num of deck) { + let i = q.pop(); + res[i] = num; + if (!q.isEmpty()) { + q.push(q.pop()); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Simuation Using Queue - II + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + q = deque() + for i in range(len(deck) - 1, -1, -1): + if q: + q.append(q.popleft()) + q.append(deck[i]) + return list(q)[::-1] +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + Queue q = new LinkedList<>(); + + for (int i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.offer(q.poll()); + } + q.offer(deck[i]); + } + + int[] res = new int[deck.length]; + for (int i = deck.length - 1; i >= 0; i--) { + res[i] = q.poll(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + queue q; + + for (int i = deck.size() - 1; i >= 0; i--) { + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + q.push(deck[i]); + } + + vector res(deck.size()); + for (int i = deck.size() - 1; i >= 0; i--) { + res[i] = q.front(); + q.pop(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const q = new Queue(); + + for (let i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.push(q.pop()); + } + q.push(deck[i]); + } + + return q.toArray().reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Simulation Using Deque + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + dq = deque() + dq.append(deck.pop()) + for i in range(len(deck) - 1, -1, -1): + dq.appendleft(dq.pop()) + dq.appendleft(deck[i]) + return list(dq) +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + Arrays.sort(deck); + Deque dq = new LinkedList<>(); + dq.addLast(deck[n - 1]); + + for (int i = n - 2; i >= 0; i--) { + dq.addFirst(dq.removeLast()); + dq.addFirst(deck[i]); + } + + return dq.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + deque dq; + dq.push_back(deck.back()); + + for (int i = deck.size() - 2; i >= 0; i--) { + dq.push_front(dq.back()); + dq.pop_back(); + dq.push_front(deck[i]); + } + + return vector(dq.begin(), dq.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const dq = new Deque([deck.pop()]); + + for (let i = deck.length - 1; i >= 0; i--) { + let val = dq.popBack(); + dq.pushFront(val); + dq.pushFront(deck[i]); + } + + let res = []; + while (!dq.isEmpty()) { + res.push(dq.popFront()); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Simulation Using Two Pointers + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + n = len(deck) + res = [0] * n + skip = False + deckIndex, i = 0, 0 + + deck.sort() + + while deckIndex < n: + if res[i] == 0: + if not skip: + res[i] = deck[deckIndex] + deckIndex += 1 + skip = not skip + i = (i + 1) % n + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + int[] res = new int[n]; + Arrays.fill(res, 0); + boolean skip = false; + int deckIndex = 0, i = 0; + + Arrays.sort(deck); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + int n = deck.size(); + vector res(n, 0); + bool skip = false; + int deckIndex = 0, i = 0; + + sort(deck.begin(), deck.end()); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + let n = deck.length; + let res = new Array(n).fill(0); + let skip = false; + let deckIndex = 0, i = 0; + + deck.sort((a, b) => a - b); + + while (deckIndex < n) { + if (res[i] === 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/rotate-list.md b/articles/rotate-list.md new file mode 100644 index 000000000..675821f82 --- /dev/null +++ b/articles/rotate-list.md @@ -0,0 +1,486 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + if not head: + return None + + arr, cur = [], head + while cur: + arr.append(cur.val) + cur = cur.next + + n = len(arr) + k %= n + cur = head + for i in range(n - k, n): + cur.val = arr[i] + cur = cur.next + + for i in range(n - k): + cur.val = arr[i] + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) return null; + + ArrayList arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + for (int i = 0; i < n - k; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur->val = arr[i]; + cur = cur->next; + } + for (int i = 0; i < n - k; i++) { + cur->val = arr[i]; + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) return null; + + let arr = []; + let cur = head; + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let n = arr.length; + k %= n; + cur = head; + for (let i = n - k; i < n; i++) { + cur.val = arr[i]; + cur = cur.next; + } + for (let i = 0; i < n - k; i++) { + cur.val = arr[i]; + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + length, tail = 1, head + while tail.next: + tail = tail.next + length += 1 + + k = k % length + if k == 0: + return head + + cur = head + for i in range(length - k - 1): + cur = cur.next + newHead = cur.next + cur.next = None + tail.next = head + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + int length = 1; + ListNode tail = head; + while (tail.next != null) { + tail = tail.next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + ListNode newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + int length = 1; + ListNode* tail = head; + while (tail->next) { + tail = tail->next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode* cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur->next; + } + ListNode* newHead = cur->next; + cur->next = nullptr; + tail->next = head; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let length = 1, tail = head; + while (tail.next) { + tail = tail.next; + length++; + } + + k = k % length; + if (k === 0) { + return head; + } + + let cur = head; + for (let i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + let newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Iteration (Using One Pointer) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + cur, n = head, 1 + while cur.next: + n += 1 + cur = cur.next + + cur.next = head + k %= n + for i in range(n - k): + cur = cur.next + + head = cur.next + cur.next = None + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + ListNode cur = head; + int n = 1; + while (cur.next != null) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + ListNode* cur = head; + int n = 1; + while (cur->next) { + n++; + cur = cur->next; + } + + cur->next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur->next; + } + + head = cur->next; + cur->next = nullptr; + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let cur = head, n = 1; + while (cur.next) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (let i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/score-after-flipping-matrix.md b/articles/score-after-flipping-matrix.md new file mode 100644 index 000000000..83c222266 --- /dev/null +++ b/articles/score-after-flipping-matrix.md @@ -0,0 +1,253 @@ +## 1. Greedy (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + for r in range(ROWS): + if grid[r][0] == 0: + for c in range(COLS): + grid[r][c] ^= 1 + + for c in range(COLS): + one_cnt = sum(grid[r][c] for r in range(ROWS)) + if one_cnt < ROWS - one_cnt: + for r in range(ROWS): + grid[r][c] ^= 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + res += grid[r][c] << (COLS - c - 1) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + let ROWS = grid.length, COLS = grid[0].length; + + for (let r = 0; r < ROWS; r++) { + if (grid[r][0] === 0) { + for (let c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (let c = 0; c < COLS; c++) { + let oneCnt = 0; + for (let r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (let r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + res = ROWS * (1 << (COLS - 1)) + + for c in range(1, COLS): + cnt = 0 + for r in range(ROWS): + if grid[r][c] != grid[r][0]: + cnt += 1 + + cnt = max(cnt, ROWS - cnt) + res += cnt * (1 << (COLS - c - 1)) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let res = ROWS * (1 << (COLS - 1)); + + for (let c = 1; c < COLS; c++) { + let cnt = 0; + for (let r = 0; r < ROWS; r++) { + if (grid[r][c] !== grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/splitting-a-string-into-descending-consecutive-values.md b/articles/splitting-a-string-into-descending-consecutive-values.md new file mode 100644 index 000000000..b736eda11 --- /dev/null +++ b/articles/splitting-a-string-into-descending-consecutive-values.md @@ -0,0 +1,570 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + + def isValid(splits): + for i in range(1, len(splits)): + if splits[i] != splits[i - 1] - 1: + return False + return len(splits) > 1 + + def dfs(i, splits): + if i == n: + return isValid(splits) + num = 0 + for j in range(i, n): + num = num * 10 + int(s[j]) + splits.append(num) + if dfs(j + 1, splits): + return True + splits.pop() + return False + + return dfs(0, []) +``` + +```java +public class Solution { + public boolean splitString(String s) { + return dfs(s, 0, new ArrayList<>()); + } + + private boolean isValid(List splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits.get(i) != splits.get(i - 1) - 1) { + return false; + } + } + return splits.size() > 1; + } + + private boolean dfs(String s, int i, List splits) { + if (i == s.length()) { + return isValid(splits); + } + long num = 0; + for (int j = i; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + splits.add(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.remove(splits.size() - 1); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + vector splits; + return dfs(s, 0, splits); + } + +private: + bool isValid(vector& splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits[i] != splits[i - 1] - 1) { + return false; + } + } + return splits.size() > 1; + } + + bool dfs(string& s, int i, vector& splits) { + if (i == s.size()) { + return isValid(splits); + } + unsigned long long num = 0; + for (int j = i; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + splits.push_back(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.pop_back(); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const isValid = (splits) => { + for (let i = 1; i < splits.length; i++) { + if (splits[i] !== splits[i - 1] - 1) { + return false; + } + } + return splits.length > 1; + }; + + const dfs = (i, splits) => { + if (i === n) { + return isValid(splits); + } + let num = 0; + for (let j = i; j < n; j++) { + num = num * 10 + Number(s[j]); + splits.push(num); + if (dfs(j + 1, splits)) { + return true; + } + splits.pop(); + } + return false; + }; + + return dfs(0, []); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion - I + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Recursion - II + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + if num >= prev: + break + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Stack + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + stack = [] + val = 0 + + for i in range(n - 1): + val = val * 10 + int(s[i]) + stack.append((i + 1, val)) + + while stack: + index, prev = stack.pop() + num = 0 + for j in range(index, n): + num = num * 10 + int(s[j]) + if num + 1 == prev: + if j + 1 == n: + return True + stack.append((j + 1, num)) + elif num >= prev: + break + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + Stack stack = new Stack<>(); + long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + stack.push(new long[]{i + 1, val}); + + while (!stack.isEmpty()) { + long[] top = stack.pop(); + int index = (int) top[0]; + long prev = top[1]; + long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push(new long[]{j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + stack> stack; + unsigned long long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + stack.push({i + 1, val}); + + while (!stack.empty()) { + auto [index, prev] = stack.top(); + stack.pop(); + unsigned long long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push({j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + let stack = []; + let val = 0; + + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + stack.push([i + 1, val]); + + while (stack.length) { + let [index, prev] = stack.pop(); + let num = 0; + + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev) { + if (j + 1 === n) { + return true; + } + stack.push([j + 1, num]); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/the-number-of-beautiful-subsets.md b/articles/the-number-of-beautiful-subsets.md new file mode 100644 index 000000000..1ed55b435 --- /dev/null +++ b/articles/the-number-of-beautiful-subsets.md @@ -0,0 +1,739 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + def helper(i, count): + if i == len(nums): + return 1 + + res = helper(i + 1, count) # Skip nums[i] + if not count[nums[i] + k] and not count[nums[i] - k]: + count[nums[i]] += 1 + res += helper(i + 1, count) + count[nums[i]] -= 1 + + return res + + return helper(0, defaultdict(int)) - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + return helper(0, new HashMap<>(), nums, k) - 1; + } + + private int helper(int i, Map count, int[] nums, int k) { + if (i == nums.length) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + + if (!count.containsKey(nums[i] + k) && !count.containsKey(nums[i] - k)) { + count.put(nums[i], count.getOrDefault(nums[i], 0) + 1); + res += helper(i + 1, count, nums, k); + count.put(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) == 0) { + count.remove(nums[i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map count; + return helper(0, count, nums, k) - 1; + } + +private: + int helper(int i, unordered_map& count, vector& nums, int k) { + if (i == nums.size()) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + if (!count[nums[i] + k] && !count[nums[i] - k]) { + count[nums[i]]++; + res += helper(i + 1, count, nums, k); + count[nums[i]]--; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + const helper = (i, count) => { + if (i === nums.length) { + return 1; + } + + let res = helper(i + 1, count); // Skip nums[i] + + if (!count.has(nums[i] + k) && !count.has(nums[i] - k)) { + count.set(nums[i], (count.get(nums[i]) || 0) + 1); + res += helper(i + 1, count); + count.set(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) === 0) { + count.delete(nums[i]); + } + } + + return res; + }; + + return helper(0, new Map()) - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + cache = {} + + def helper(n, g): + if n not in g: + return 1 + if n in cache: + return cache[n] + + skip = helper(n + k, g) + include = (2**g[n] - 1) * helper(n + 2 * k, g) + cache[n] = skip + include + return skip + include + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + n = min(g.keys()) + res *= helper(n, g) + + return res - 1 +``` + +```java +public class Solution { + private Map cache; + private Map cnt; + private Set visit; + + public int beautifulSubsets(int[] nums, int k) { + List> groups = new ArrayList<>(); + this.cache = new HashMap<>(); + this.cnt = new HashMap<>(); + this.visit = new HashSet<>(); + + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + int n = Collections.min(g.keySet()); + res *= helper(n, g, k); + } + + return res - 1; + } + + private int helper(int n, Map g, int k) { + if (!g.containsKey(n)) { + return 1; + } + if (cache.containsKey(n)) { + return cache.get(n); + } + + int skip = helper(n + k, g, k); + int include = (int) ((Math.pow(2, g.get(n)) - 1) * helper(n + 2 * k, g, k)); + int result = skip + include; + cache.put(n, result); + return result; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + vector> groups; + cache.clear(); + cnt.clear(); + visit.clear(); + + for (int& num : nums) { + cnt[num]++; + } + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + int n = min_element(g.begin(), g.end())->first; + res *= helper(n, g, k); + } + return res - 1; + } + +private: + unordered_map cache; + unordered_map cnt; + unordered_set visit; + + int helper(int n, unordered_map& g, int k) { + if (!g.count(n)) { + return 1; + } + if (cache.count(n)) { + return cache[n]; + } + + int skip = helper(n + k, g, k); + int include = (pow(2, g[n]) - 1) * helper(n + 2 * k, g, k); + return cache[n] = skip + include; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let cache = new Map(); + let visit = new Set(); + + for (let n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + while (cnt.has(n - k)) { + n -= k; + } + while (cnt.has(n)) { + g.set(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.push(g); + } + + const helper = (n, g) => { + if (!g.has(n)) { + return 1; + } + if (cache.has(n)) { + return cache.get(n); + } + + let skip = helper(n + k, g); + let include = (2 ** g.get(n) - 1) * helper(n + 2 * k, g); + let result = skip + include; + cache.set(n, result); + return result; + }; + + let res = 1; + for (const g of groups) { + let n = Math.min(...g.keys()); + res *= helper(n, g); + } + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + dp = {} + prev = None + + for num in sorted(g): + count = g[num] + if prev is None or prev + k != num: + dp[num] = (dp.get(prev, 1) * (1 + (2 ** count - 1))) + else: + dp[num] = dp[prev] + (2 ** count - 1) * dp.get(prev - k, 1) + prev = num + + res *= dp[prev] + + return res - 1 +``` + +```java +class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + List> groups = new ArrayList<>(); + Set visit = new HashSet<>(); + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + Map dp = new HashMap<>(); + Integer prev = null; + + List arr = new ArrayList<>(g.keySet()); + Collections.sort(arr); + for (int num : arr) { + int count = g.get(num); + if (prev == null || prev + k != num) { + dp.put(num, dp.getOrDefault(prev, 1) * (1 + (int) Math.pow(2, count) - 1)); + } else { + dp.put(num, dp.get(prev) + + ((int) Math.pow(2, count) - 1) * dp.getOrDefault(prev - k, 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map cnt; + for (int num : nums) { + cnt[num]++; + } + + vector> groups; + unordered_set visit; + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + unordered_map dp; + int prev = -1; + + vector keys; + for (auto& [num, _] : g) { + keys.push_back(num); + } + sort(keys.begin(), keys.end()); + + for (int num : keys) { + int count = g[num]; + if (prev == -1 || prev + k != num) { + dp[num] = dp.count(prev) ? dp[prev] * (1 + (1 << count) - 1) : + (1 + (1 << count) - 1); + } else { + dp[num] = dp[prev] + ((1 << count) - 1) * + (dp.count(prev - k) ? dp[prev - k] : 1); + } + prev = num; + } + + res *= dp[prev]; + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let visit = new Set(); + + for (const n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + let num = n; + while (cnt.has(num - k)) { + num -= k; + } + while (cnt.has(num)) { + g.set(num, cnt.get(num)); + visit.add(num); + num += k; + } + groups.push(g); + } + + let res = 1; + for (const g of groups) { + let dp = new Map(); + let prev = null; + + for (const num of g.keys()) { + let count = g.get(num); + if (prev === null || prev + k !== num) { + dp.set(num, (dp.get(prev) || 1) * (1 + (2 ** count - 1))); + } else { + dp.set(num, dp.get(prev) + (2 ** count - 1) * (dp.get(prev - k) || 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = defaultdict(dict) + + # Group numbers based on their remainder with k + for num in nums: + groups[num % k][num] = cnt[num] + + res = 1 + for g in groups.values(): + prev = 0 + dp, ndp = 0, 1 + + for num in sorted(g.keys()): + count = g[num] + have = (1 << count) - 1 + tmp = ndp + ndp += dp + + if prev == 0 or prev + k != num: + dp = have * (tmp + dp) + else: + dp = tmp * have + + prev = num + + res *= (dp + ndp) + + return res - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map> groups = new HashMap<>(); + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups.putIfAbsent(num % k, new HashMap<>()); + groups.get(num % k).put(num, cnt.get(num)); + } + + int res = 1; + for (Map g : groups.values()) { + int prev = 0, dp = 0, ndp = 1; + List sortedKeys = new ArrayList<>(g.keySet()); + Collections.sort(sortedKeys); + + for (int num : sortedKeys) { + int count = g.get(num); + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map> groups; + unordered_map cnt; + for (int& num : nums) { + cnt[num]++; + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups[num % k][num] = cnt[num]; + } + + int res = 1; + for (auto& [rem, g] : groups) { + int prev = 0, dp = 0, ndp = 1; + + for (auto& [num, count] : g) { + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let groups = new Map(); + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + // Group numbers based on remainder with k + for (const num of nums) { + if (!groups.has(num % k)) { + groups.set(num % k, new Map()); + } + groups.get(num % k).set(num, cnt.get(num)); + } + + let res = 1; + for (const g of groups.values()) { + let prev = 0, dp = 0, ndp = 1; + let sortedKeys = Array.from(g.keys()).sort((a, b) => a - b); + + for (const num of sortedKeys) { + let count = g.get(num); + let have = (1 << count) - 1; + let tmp = ndp; + ndp += dp; + + if (prev === 0 || prev + k !== num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/two-city-scheduling.md b/articles/two-city-scheduling.md new file mode 100644 index 000000000..61f80767c --- /dev/null +++ b/articles/two-city-scheduling.md @@ -0,0 +1,683 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the size of the array $costs$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + if dp[aCount][bCount] != -1: + return dp[aCount][bCount] + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + + dp[aCount][bCount] = res + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + private int[][] dp; + + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + dp = new int[n + 1][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + dp = vector>(n + 1, vector(n + 1, -1)); + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + if (dp[aCount][bCount] !== -1) { + return dp[aCount][bCount]; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[0] * (n + 1) for _ in range(n + 1)] + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + dp[aCount][bCount] = float("inf") + if aCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]) + if bCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]) + + return dp[n][n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[][] dp = new int[n + 1][n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector> dp(n + 1, vector(n + 1)); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = INT_MAX; + if (aCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0)); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + dp[aCount][bCount] = Infinity; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [0] * (n + 1) + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + tmp = dp[bCount] + dp[bCount] = float("inf") + if aCount > 0: + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]) + if bCount > 0: + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]) + + return dp[n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[] dp = new int[n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector dp(n + 1, 0); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = INT_MAX; + if (aCount > 0) { + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = new Array(n + 1).fill(0); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + let tmp = dp[bCount]; + dp[bCount] = Infinity; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + diffs = [] + for c1, c2 in costs: + diffs.append([c2 - c1, c1, c2]) + + diffs.sort() + res = 0 + for i in range(len(diffs)): + if i < len(diffs) // 2: + res += diffs[i][2] + else: + res += diffs[i][1] + + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + List diffs = new ArrayList<>(); + for (int[] cost : costs) { + diffs.add(new int[]{cost[1] - cost[0], cost[0], cost[1]}); + } + + diffs.sort(Comparator.comparingInt(a -> a[0])); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs.get(i)[2]; + } else { + res += diffs.get(i)[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + vector> diffs; + for (auto& cost : costs) { + diffs.push_back({cost[1] - cost[0], cost[0], cost[1]}); + } + + sort(diffs.begin(), diffs.end()); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let diffs = []; + for (let cost of costs) { + diffs.push([cost[1] - cost[0], cost[0], cost[1]]); + } + + diffs.sort((a, b) => a[0] - b[0]); + + let res = 0; + for (let i = 0; i < diffs.length; i++) { + if (i < diffs.length / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + costs.sort(key=lambda x: x[1] - x[0]) + n, res = len(costs) // 2, 0 + + for i in range(n): + res += costs[i][1] + costs[i + n][0] + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + Arrays.sort(costs, (a, b) -> Integer.compare(a[1] - a[0], b[1] - b[0])); + int n = costs.length / 2, res = 0; + + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + sort(costs.begin(), costs.end(), [](const auto& a, const auto& b) { + return (a[1] - a[0]) < (b[1] - b[0]); + }); + + int n = costs.size() / 2, res = 0; + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + costs.sort((a, b) => (a[1] - a[0]) - (b[1] - b[0])); + let n = costs.length / 2, res = 0; + + for (let i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/widest-vertical-area-between-two-points-containing-no-points.md b/articles/widest-vertical-area-between-two-points-containing-no-points.md new file mode 100644 index 000000000..b41a574f7 --- /dev/null +++ b/articles/widest-vertical-area-between-two-points-containing-no-points.md @@ -0,0 +1,212 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + n = len(points) + res = 0 + + for i in range(1, n): + x1 = points[i][0] + for j in range(i): + x2 = points[j][0] + hasPoints = False + for k in range(n): + if k == i or k == j: + continue + + x3 = points[k][0] + if x3 > min(x1, x2) and x3 < max(x1, x2): + hasPoints = True + break + + if not hasPoints: + res = max(res, abs(x1 - x2)) + + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + int n = points.length, res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + boolean hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + int n = points.size(), res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + bool hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > min(x1, x2) && x3 < max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = max(res, abs(x1 - x2)); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + let n = points.length, res = 0; + + for (let i = 1; i < n; i++) { + let x1 = points[i][0]; + for (let j = 0; j < i; j++) { + let x2 = points[j][0]; + let hasPoints = false; + + for (let k = 0; k < n; k++) { + if (k === i || k === j) continue; + + let x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + points.sort() + res = 0 + for i in range(len(points) - 1): + res = max(res, points[i + 1][0] - points[i][0]) + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + Arrays.sort(points, Comparator.comparingInt(a -> a[0])); + int res = 0; + + for (int i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[0] < b[0]; + }); + + int res = 0; + for (int i = 0; i < points.size() - 1; i++) { + res = max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + points.sort((a, b) => a[0] - b[0]); + let res = 0; + + for (let i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file