diff --git a/articles/add-two-numbers-ii.md b/articles/add-two-numbers-ii.md new file mode 100644 index 000000000..6ccb8b95b --- /dev/null +++ b/articles/add-two-numbers-ii.md @@ -0,0 +1,468 @@ +## 1. Reverse List + +::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 addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + def reverseList(head): + prev, curr = None, head + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + return prev + + l1 = reverseList(l1) + l2 = reverseList(l2) + head = None + carry = 0 + + while l1 or l2 or carry: + v1 = l1.val if l1 else 0 + v2 = l2.val if l2 else 0 + total = v1 + v2 + carry + carry = total // 10 + node = ListNode(total % 10) + node.next = head + head = node + l1 = l1.next if l1 else None + l2 = l2.next if l2 else 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 { + private ListNode reverseList(ListNode head) { + ListNode prev = null, curr = head; + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } + + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + l1 = reverseList(l1); + l2 = reverseList(l2); + ListNode head = null; + int carry = 0; + + while (l1 != null || l2 != null || carry > 0) { + int v1 = l1 != null ? l1.val : 0; + int v2 = l2 != null ? l2.val : 0; + int total = v1 + v2 + carry; + carry = total / 10; + ListNode node = new ListNode(total % 10); + node.next = head; + head = node; + l1 = l1 != null ? l1.next : null; + l2 = l2 != null ? l2.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 { + ListNode* reverseList(ListNode* head) { + ListNode* prev = nullptr; + ListNode* curr = head; + while (curr) { + ListNode* temp = curr->next; + curr->next = prev; + prev = curr; + curr = temp; + } + return prev; + } + +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + l1 = reverseList(l1); + l2 = reverseList(l2); + ListNode* head = nullptr; + int carry = 0; + + while (l1 || l2 || carry) { + int v1 = l1 ? l1->val : 0; + int v2 = l2 ? l2->val : 0; + int total = v1 + v2 + carry; + carry = total / 10; + ListNode* node = new ListNode(total % 10); + node->next = head; + head = node; + l1 = l1 ? l1->next : nullptr; + l2 = l2 ? l2->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} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + addTwoNumbers(l1, l2) { + const reverseList = (head) => { + let prev = null, curr = head; + while (curr) { + let temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } + + l1 = reverseList(l1); + l2 = reverseList(l2); + let head = null; + let carry = 0; + + while (l1 || l2 || carry) { + let v1 = l1 ? l1.val : 0; + let v2 = l2 ? l2.val : 0; + let total = v1 + v2 + carry; + carry = Math.floor(total / 10); + let node = new ListNode(total % 10); + node.next = head; + head = node; + l1 = l1 ? l1.next : null; + l2 = l2 ? l2.next : null; + } + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + private ListNode ReverseList(ListNode head) { + ListNode prev = null, curr = head; + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } + + public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { + l1 = ReverseList(l1); + l2 = ReverseList(l2); + ListNode head = null; + int carry = 0; + + while (l1 != null || l2 != null || carry != 0) { + int v1 = l1 != null ? l1.val : 0; + int v2 = l2 != null ? l2.val : 0; + int total = v1 + v2 + carry; + carry = total / 10; + ListNode node = new ListNode(total % 10); + node.next = head; + head = node; + l1 = l1 != null ? l1.next : null; + l2 = l2 != null ? l2.next : null; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(max(m, n))$ for the output list. + +> Where $m$ is the length of $l1$ and $n$ is the length of $l2$. + +--- + +## 2. Stack + +::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 addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + s1, s2 = [], [] + + while l1: + s1.append(l1.val) + l1 = l1.next + + while l2: + s2.append(l2.val) + l2 = l2.next + + carry = 0 + head = None + + while s1 or s2 or carry: + v1 = s1.pop() if s1 else 0 + v2 = s2.pop() if s2 else 0 + total = v1 + v2 + carry + carry = total // 10 + node = ListNode(total % 10) + node.next = head + head = node + + 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 addTwoNumbers(ListNode l1, ListNode l2) { + Stack s1 = new Stack<>(); + Stack s2 = new Stack<>(); + + while (l1 != null) { + s1.push(l1.val); + l1 = l1.next; + } + + while (l2 != null) { + s2.push(l2.val); + l2 = l2.next; + } + + int carry = 0; + ListNode head = null; + + while (!s1.isEmpty() || !s2.isEmpty() || carry > 0) { + int v1 = s1.isEmpty() ? 0 : s1.pop(); + int v2 = s2.isEmpty() ? 0 : s2.pop(); + int total = v1 + v2 + carry; + carry = total / 10; + ListNode node = new ListNode(total % 10); + node.next = head; + head = node; + } + + 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* addTwoNumbers(ListNode* l1, ListNode* l2) { + stack s1, s2; + + while (l1) { + s1.push(l1->val); + l1 = l1->next; + } + + while (l2) { + s2.push(l2->val); + l2 = l2->next; + } + + int carry = 0; + ListNode* head = nullptr; + + while (!s1.empty() || !s2.empty() || carry) { + int v1 = s1.empty() ? 0 : s1.top(); if (!s1.empty()) s1.pop(); + int v2 = s2.empty() ? 0 : s2.top(); if (!s2.empty()) s2.pop(); + int total = v1 + v2 + carry; + carry = total / 10; + ListNode* node = new ListNode(total % 10); + node->next = head; + head = node; + } + + 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} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + addTwoNumbers(l1, l2) { + const s1 = [], s2 = []; + + while (l1) { + s1.push(l1.val); + l1 = l1.next; + } + + while (l2) { + s2.push(l2.val); + l2 = l2.next; + } + + let carry = 0; + let head = null; + + while (s1.length || s2.length || carry) { + const v1 = s1.length ? s1.pop() : 0; + const v2 = s2.length ? s2.pop() : 0; + const total = v1 + v2 + carry; + carry = Math.floor(total / 10); + const node = new ListNode(total % 10); + node.next = head; + head = node; + } + + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { + Stack s1 = new Stack(); + Stack s2 = new Stack(); + + while (l1 != null) { + s1.Push(l1.val); + l1 = l1.next; + } + + while (l2 != null) { + s2.Push(l2.val); + l2 = l2.next; + } + + int carry = 0; + ListNode head = null; + + while (s1.Count > 0 || s2.Count > 0 || carry > 0) { + int v1 = s1.Count > 0 ? s1.Pop() : 0; + int v2 = s2.Count > 0 ? s2.Pop() : 0; + int total = v1 + v2 + carry; + carry = total / 10; + ListNode node = new ListNode(total % 10); + node.next = head; + head = node; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of $l1$ and $n$ is the length of $l2$. \ No newline at end of file diff --git a/articles/custom-sort-string.md b/articles/custom-sort-string.md new file mode 100644 index 000000000..2a6df0b0d --- /dev/null +++ b/articles/custom-sort-string.md @@ -0,0 +1,263 @@ +## 1. Custom Comparator + +::tabs-start + +```python +class Solution: + def customSortString(self, order: str, s: str) -> str: + rank = {c: i for i, c in enumerate(order)} + return ''.join(sorted(s, key=lambda c: rank.get(c, 26))) +``` + +```java +public class Solution { + public String customSortString(String order, String s) { + int[] rank = new int[26]; + for (int i = 0; i < order.length(); i++) { + rank[order.charAt(i) - 'a'] = i + 1; + } + + Character[] arr = new Character[s.length()]; + for (int i = 0; i < s.length(); i++) { + arr[i] = s.charAt(i); + } + + Arrays.sort(arr, (a, b) -> rank[a - 'a'] - rank[b - 'a']); + + StringBuilder sb = new StringBuilder(); + for (char c : arr) { + sb.append(c); + } + return sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string customSortString(string order, string s) { + vector rank(26, 26); + for (int i = 0; i < order.size(); ++i) { + rank[order[i] - 'a'] = i; + } + + sort(s.begin(), s.end(), [&](char a, char b) { + return rank[a - 'a'] < rank[b - 'a']; + }); + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} order + * @param {string} s + * @return {string} + */ + customSortString(order, s) { + const rank = {}; + for (let i = 0; i < order.length; i++) { + rank[order[i]] = i; + } + + return [...s].sort((a, b) => { + const ra = rank[a] ?? 26; + const rb = rank[b] ?? 26; + return ra - rb; + }).join(''); + } +} +``` + +```csharp +public class Solution { + public string CustomSortString(string order, string s) { + Dictionary rank = new Dictionary(); + for (int i = 0; i < order.Length; i++) { + rank[order[i]] = i; + } + + char[] arr = s.ToCharArray(); + Array.Sort(arr, (a, b) => { + int ra = rank.ContainsKey(a) ? rank[a] : 26; + int rb = rank.ContainsKey(b) ? rank[b] : 26; + return ra - rb; + }); + + return new string(arr); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Frequency Count + +::tabs-start + +```python +class Solution: + def customSortString(self, order: str, s: str) -> str: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + res = [] + for c in order: + idx = ord(c) - ord('a') + while count[idx]: + res.append(c) + count[idx] -= 1 + + for idx in range(26): + c = chr(ord('a') + idx) + while count[idx]: + count[idx] -= 1 + res.append(c) + + return ''.join(res) +``` + +```java +public class Solution { + public String customSortString(String order, String s) { + int[] count = new int[26]; + for (char c : s.toCharArray()) { + count[c - 'a']++; + } + + StringBuilder res = new StringBuilder(); + for (char c : order.toCharArray()) { + int idx = c - 'a'; + while (count[idx] > 0) { + res.append(c); + count[idx]--; + } + } + + for (int idx = 0; idx < 26; idx++) { + char c = (char) ('a' + idx); + while (count[idx] > 0) { + res.append(c); + count[idx]--; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string customSortString(string order, string s) { + vector count(26, 0); + for (char c : s) { + count[c - 'a']++; + } + + string res; + for (char c : order) { + int idx = c - 'a'; + while (count[idx] > 0) { + res += c; + count[idx]--; + } + } + + for (int idx = 0; idx < 26; ++idx) { + char c = 'a' + idx; + while (count[idx] > 0) { + res += c; + count[idx]--; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} order + * @param {string} s + * @return {string} + */ + customSortString(order, s) { + const count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 97]++; + } + + const res = []; + for (let c of order) { + let idx = c.charCodeAt(0) - 97; + while (count[idx] > 0) { + res.push(c); + count[idx]--; + } + } + + for (let idx = 0; idx < 26; idx++) { + let c = String.fromCharCode(97 + idx); + while (count[idx] > 0) { + res.push(c); + count[idx]--; + } + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string CustomSortString(string order, string s) { + int[] count = new int[26]; + foreach (char c in s) { + count[c - 'a']++; + } + + StringBuilder res = new StringBuilder(); + foreach (char c in order) { + int idx = c - 'a'; + while (count[idx] > 0) { + res.Append(c); + count[idx]--; + } + } + + for (int idx = 0; idx < 26; idx++) { + char c = (char)('a' + idx); + while (count[idx] > 0) { + res.Append(c); + count[idx]--; + } + } + + return res.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-lucky-integer-in-an-array.md b/articles/find-lucky-integer-in-an-array.md new file mode 100644 index 000000000..02ffa2cea --- /dev/null +++ b/articles/find-lucky-integer-in-an-array.md @@ -0,0 +1,581 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findLucky(self, arr: List[int]) -> int: + res = -1 + + for num in arr: + cnt = 0 + for a in arr: + if num == a: + cnt += 1 + if cnt == num: + res = max(res, num) + + return res +``` + +```java +public class Solution { + public int findLucky(int[] arr) { + int res = -1; + + for (int num : arr) { + int cnt = 0; + for (int a : arr) { + if (num == a) { + cnt++; + } + } + if (cnt == num) { + res = Math.max(res, num); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLucky(vector& arr) { + int res = -1; + + for (int num : arr) { + int cnt = 0; + for (int a : arr) { + if (num == a) { + cnt++; + } + } + if (cnt == num) { + res = max(res, num); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + findLucky(arr) { + let res = -1; + + for (let num of arr) { + let cnt = 0; + for (let a of arr) { + if (num === a) { + cnt++; + } + } + if (cnt === num) { + res = Math.max(res, num); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int FindLucky(int[] arr) { + int res = -1; + + foreach (int num in arr) { + int cnt = 0; + foreach (int a in arr) { + if (num == a) { + cnt++; + } + } + if (cnt == num) { + res = Math.Max(res, num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findLucky(self, arr: List[int]) -> int: + arr.sort() + streak = 0 + for i in range(len(arr) - 1, -1, -1): + streak += 1 + if i == 0 or (arr[i] != arr[i - 1]): + if arr[i] == streak: + return arr[i] + streak = 0 + return -1 +``` + +```java +public class Solution { + public int findLucky(int[] arr) { + Arrays.sort(arr); + int streak = 0; + + for (int i = arr.length - 1; i >= 0; i--) { + streak++; + if (i == 0 || arr[i] != arr[i - 1]) { + if (arr[i] == streak) { + return arr[i]; + } + streak = 0; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findLucky(vector& arr) { + sort(arr.begin(), arr.end()); + int streak = 0; + + for (int i = arr.size() - 1; i >= 0; i--) { + streak++; + if (i == 0 || arr[i] != arr[i - 1]) { + if (arr[i] == streak) { + return arr[i]; + } + streak = 0; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + findLucky(arr) { + arr.sort((a, b) => a - b); + let streak = 0; + + for (let i = arr.length - 1; i >= 0; i--) { + streak++; + if (i === 0 || arr[i] !== arr[i - 1]) { + if (arr[i] === streak) { + return arr[i]; + } + streak = 0; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindLucky(int[] arr) { + Array.Sort(arr); + int streak = 0; + + for (int i = arr.Length - 1; i >= 0; i--) { + streak++; + if (i == 0 || arr[i] != arr[i - 1]) { + if (arr[i] == streak) { + return arr[i]; + } + streak = 0; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def findLucky(self, arr: List[int]) -> int: + cnt = Counter(arr) + res = -1 + + for num in cnt: + if num == cnt[num]: + res = max(num, res) + + return res +``` + +```java +public class Solution { + public int findLucky(int[] arr) { + Map count = new HashMap<>(); + for (int num : arr) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int res = -1; + for (int num : count.keySet()) { + if (num == count.get(num)) { + res = Math.max(res, num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLucky(vector& arr) { + unordered_map count; + for (int num : arr) { + count[num]++; + } + + int res = -1; + for (auto& [num, freq] : count) { + if (num == freq) { + res = max(res, num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + findLucky(arr) { + const count = new Map(); + for (const num of arr) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = -1; + for (const [num, freq] of count.entries()) { + if (num === freq) { + res = Math.max(res, num); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int FindLucky(int[] arr) { + Dictionary count = new Dictionary(); + foreach (int num in arr) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + int res = -1; + foreach (var kvp in count) { + if (kvp.Key == kvp.Value) { + res = Math.Max(res, kvp.Key); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findLucky(self, arr: List[int]) -> int: + n = len(arr) + for i in range(n): + prev, num = i, arr[i] + while 0 < num <= n: + nxt = arr[num - 1] + arr[num - 1] = min(0, arr[num - 1]) - 1 + if num - 1 <= i or num - 1 == prev: + break + prev = num - 1 + num = nxt + + for i in range(n - 1, -1, -1): + if -arr[i] == i + 1: + return i + 1 + + return -1 +``` + +```java +public class Solution { + public int findLucky(int[] arr) { + int n = arr.length; + for (int i = 0; i < n; i++) { + int prev = i, num = arr[i]; + while (0 < num && num <= n) { + int nxt = arr[num - 1]; + arr[num - 1] = Math.min(0, arr[num - 1]) - 1; + if (num - 1 <= i || num - 1 == prev) break; + prev = num - 1; + num = nxt; + } + } + + for (int i = n - 1; i >= 0; i--) { + if (-arr[i] == i + 1) return i + 1; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findLucky(vector& arr) { + int n = arr.size(); + for (int i = 0; i < n; i++) { + int prev = i, num = arr[i]; + while (0 < num && num <= n) { + int nxt = arr[num - 1]; + arr[num - 1] = min(0, arr[num - 1]) - 1; + if (num - 1 <= i || num - 1 == prev) break; + prev = num - 1; + num = nxt; + } + } + + for (int i = n - 1; i >= 0; i--) { + if (-arr[i] == i + 1) return i + 1; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + findLucky(arr) { + const n = arr.length; + for (let i = 0; i < n; i++) { + let prev = i, num = arr[i]; + while (0 < num && num <= n) { + let nxt = arr[num - 1]; + arr[num - 1] = Math.min(0, arr[num - 1]) - 1; + if (num - 1 <= i || num - 1 === prev) break; + prev = num - 1; + num = nxt; + } + } + + for (let i = n - 1; i >= 0; i--) { + if (-arr[i] === i + 1) return i + 1; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindLucky(int[] arr) { + int n = arr.Length; + for (int i = 0; i < n; i++) { + int prev = i, num = arr[i]; + while (0 < num && num <= n) { + int nxt = arr[num - 1]; + arr[num - 1] = Math.Min(0, arr[num - 1]) - 1; + if (num - 1 <= i || num - 1 == prev) break; + prev = num - 1; + num = nxt; + } + } + + for (int i = n - 1; i >= 0; i--) { + if (-arr[i] == i + 1) return i + 1; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Bit Manipulation + +::tabs-start + +```python +class Solution: + def findLucky(self, arr: List[int]) -> int: + for num in arr: + idx = num & ((1 << 10) - 1) + if idx <= len(arr): + arr[idx - 1] += (1 << 10) + + for i in range(len(arr) - 1, -1, -1): + cnt = arr[i] >> 10 + if cnt == i + 1: + return i + 1 + return -1 +``` + +```java +public class Solution { + public int findLucky(int[] arr) { + for (int num : arr) { + int idx = num & ((1 << 10) - 1); + if (idx <= arr.length) { + arr[idx - 1] += (1 << 10); + } + } + + for (int i = arr.length - 1; i >= 0; i--) { + int cnt = arr[i] >> 10; + if (cnt == i + 1) return i + 1; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findLucky(vector& arr) { + for (int num : arr) { + int idx = num & ((1 << 10) - 1); + if (idx <= arr.size()) { + arr[idx - 1] += (1 << 10); + } + } + + for (int i = arr.size() - 1; i >= 0; i--) { + int cnt = arr[i] >> 10; + if (cnt == i + 1) return i + 1; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + findLucky(arr) { + for (let num of arr) { + const idx = num & ((1 << 10) - 1); + if (idx <= arr.length) { + arr[idx - 1] += (1 << 10); + } + } + + for (let i = arr.length - 1; i >= 0; i--) { + const cnt = arr[i] >> 10; + if (cnt === i + 1) return i + 1; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindLucky(int[] arr) { + foreach (int num in arr) { + int idx = num & ((1 << 10) - 1); + if (idx <= arr.Length) { + arr[idx - 1] += (1 << 10); + } + } + + for (int i = arr.Length - 1; i >= 0; i--) { + int cnt = arr[i] >> 10; + if (cnt == i + 1) return i + 1; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/interval-list-intersections.md b/articles/interval-list-intersections.md new file mode 100644 index 000000000..4e923ac83 --- /dev/null +++ b/articles/interval-list-intersections.md @@ -0,0 +1,425 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]: + res = [] + for i in range(len(firstList)): + startA, endA = firstList[i][0], firstList[i][1] + for j in range(len(secondList)): + startB, endB = secondList[j][0], secondList[j][1] + if (startA <= startB <= endA) or (startB <= startA <= endB): + res.append([max(startA, startB), min(endA, endB)]) + return res +``` + +```java +public class Solution { + public int[][] intervalIntersection(int[][] firstList, int[][] secondList) { + List res = new ArrayList<>(); + for (int i = 0; i < firstList.length; i++) { + int startA = firstList[i][0], endA = firstList[i][1]; + for (int j = 0; j < secondList.length; j++) { + int startB = secondList[j][0], endB = secondList[j][1]; + if ((startA <= startB && startB <= endA) || (startB <= startA && startA <= endB)) { + res.add(new int[]{Math.max(startA, startB), Math.min(endA, endB)}); + } + } + } + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> intervalIntersection(vector>& firstList, vector>& secondList) { + vector> res; + for (int i = 0; i < firstList.size(); i++) { + int startA = firstList[i][0], endA = firstList[i][1]; + for (int j = 0; j < secondList.size(); j++) { + int startB = secondList[j][0], endB = secondList[j][1]; + if ((startA <= startB && startB <= endA) || (startB <= startA && startA <= endB)) { + res.push_back({max(startA, startB), min(endA, endB)}); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} firstList + * @param {number[][]} secondList + * @return {number[][]} + */ + intervalIntersection(firstList, secondList) { + const res = []; + for (let i = 0; i < firstList.length; i++) { + const [startA, endA] = firstList[i]; + for (let j = 0; j < secondList.length; j++) { + const [startB, endB] = secondList[j]; + if ((startA <= startB && startB <= endA) || (startB <= startA && startA <= endB)) { + res.push([Math.max(startA, startB), Math.min(endA, endB)]); + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] IntervalIntersection(int[][] firstList, int[][] secondList) { + var res = new List(); + for (int i = 0; i < firstList.Length; i++) { + int startA = firstList[i][0], endA = firstList[i][1]; + for (int j = 0; j < secondList.Length; j++) { + int startB = secondList[j][0], endB = secondList[j][1]; + if ((startA <= startB && startB <= endA) || (startB <= startA && startA <= endB)) { + res.Add(new int[] { Math.Max(startA, startB), Math.Min(endA, endB) }); + } + } + } + return res.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m + n)$ for the output list. + +> Where $m$ and $n$ are the sizes of the arrays $firstList$ and $secondList$, respectively. + +--- + +## 2. Line Sweep + +::tabs-start + +```python +class Solution: + def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]: + mp = defaultdict(int) + for s, e in firstList: + mp[s] += 1 + mp[e + 1] -= 1 + for s, e in secondList: + mp[s] += 1 + mp[e + 1] -= 1 + + res = [] + active = 0 + prev = None + for x in sorted(mp): + if active == 2: + res.append([prev, x - 1]) + active += mp[x] + prev = x + return res +``` + +```java +class Solution { + public int[][] intervalIntersection(int[][] firstList, int[][] secondList) { + TreeMap mp = new TreeMap<>(); + for (int[] f : firstList) { + mp.put(f[0], mp.getOrDefault(f[0], 0) + 1); + mp.put(f[1] + 1, mp.getOrDefault(f[1] + 1, 0) - 1); + } + for (int[] s : secondList) { + mp.put(s[0], mp.getOrDefault(s[0], 0) + 1); + mp.put(s[1] + 1, mp.getOrDefault(s[1] + 1, 0) - 1); + } + + List res = new ArrayList<>(); + int active = 0, prev = 0; + boolean started = false; + for (int x : mp.keySet()) { + if (active == 2) { + res.add(new int[]{prev, x - 1}); + } + active += mp.get(x); + prev = x; + } + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> intervalIntersection(vector>& firstList, vector>& secondList) { + map mp; + for (auto& f : firstList) { + mp[f[0]] += 1; + mp[f[1] + 1] -= 1; + } + for (auto& s : secondList) { + mp[s[0]] += 1; + mp[s[1] + 1] -= 1; + } + + vector> res; + int active = 0, prev = 0; + bool started = false; + for (auto& [x, v] : mp) { + if (active == 2) { + res.push_back({prev, x - 1}); + } + active += v; + prev = x; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} firstList + * @param {number[][]} secondList + * @return {number[][]} + */ + intervalIntersection(firstList, secondList) { + let mp = new Map(); + for (let [s, e] of firstList) { + mp.set(s, (mp.get(s) || 0) + 1); + mp.set(e + 1, (mp.get(e + 1) || 0) - 1); + } + for (let [s, e] of secondList) { + mp.set(s, (mp.get(s) || 0) + 1); + mp.set(e + 1, (mp.get(e + 1) || 0) - 1); + } + + let keys = Array.from(mp.keys()).sort((a, b) => a - b); + let res = [], active = 0, prev = 0; + for (let x of keys) { + if (active === 2) { + res.push([prev, x - 1]); + } + active += mp.get(x); + prev = x; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] IntervalIntersection(int[][] firstList, int[][] secondList) { + var mp = new SortedDictionary(); + foreach (var f in firstList) { + if (!mp.ContainsKey(f[0])) mp[f[0]] = 0; + mp[f[0]] += 1; + if (!mp.ContainsKey(f[1] + 1)) mp[f[1] + 1] = 0; + mp[f[1] + 1] -= 1; + } + foreach (var s in secondList) { + if (!mp.ContainsKey(s[0])) mp[s[0]] = 0; + mp[s[0]] += 1; + if (!mp.ContainsKey(s[1] + 1)) mp[s[1] + 1] = 0; + mp[s[1] + 1] -= 1; + } + + var res = new List(); + int active = 0, prev = 0; + foreach (var kvp in mp) { + int x = kvp.Key; + if (active == 2) { + res.Add(new int[] { prev, x - 1 }); + } + active += kvp.Value; + prev = x; + } + return res.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) \log (m + n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the sizes of the arrays $firstList$ and $secondList$, respectively. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]: + res = [] + i = j = 0 + while i < len(firstList) and j < len(secondList): + startA, endA = firstList[i] + startB, endB = secondList[j] + + start = max(startA, startB) + end = min(endA, endB) + + if start <= end: + res.append([start, end]) + + if endA < endB: + i += 1 + else: + j += 1 + + return res +``` + +```java +public class Solution { + public int[][] intervalIntersection(int[][] firstList, int[][] secondList) { + List res = new ArrayList<>(); + int i = 0, j = 0; + + while (i < firstList.length && j < secondList.length) { + int startA = firstList[i][0], endA = firstList[i][1]; + int startB = secondList[j][0], endB = secondList[j][1]; + + int start = Math.max(startA, startB); + int end = Math.min(endA, endB); + + if (start <= end) { + res.add(new int[]{start, end}); + } + + if (endA < endB) { + i++; + } else { + j++; + } + } + + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> intervalIntersection(vector>& firstList, vector>& secondList) { + vector> res; + int i = 0, j = 0; + + while (i < firstList.size() && j < secondList.size()) { + int startA = firstList[i][0], endA = firstList[i][1]; + int startB = secondList[j][0], endB = secondList[j][1]; + + int start = max(startA, startB); + int end = min(endA, endB); + + if (start <= end) { + res.push_back({start, end}); + } + + if (endA < endB) { + i++; + } else { + j++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} firstList + * @param {number[][]} secondList + * @return {number[][]} + */ + intervalIntersection(firstList, secondList) { + const res = []; + let i = 0, j = 0; + + while (i < firstList.length && j < secondList.length) { + const [startA, endA] = firstList[i]; + const [startB, endB] = secondList[j]; + + const start = Math.max(startA, startB); + const end = Math.min(endA, endB); + + if (start <= end) { + res.push([start, end]); + } + + if (endA < endB) { + i++; + } else { + j++; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] IntervalIntersection(int[][] firstList, int[][] secondList) { + var res = new List(); + int i = 0, j = 0; + + while (i < firstList.Length && j < secondList.Length) { + int startA = firstList[i][0], endA = firstList[i][1]; + int startB = secondList[j][0], endB = secondList[j][1]; + + int start = Math.Max(startA, startB); + int end = Math.Min(endA, endB); + + if (start <= end) { + res.Add(new int[] { start, end }); + } + + if (endA < endB) { + i++; + } else { + j++; + } + } + + return res.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m + n)$ for the output list. + +> Where $m$ and $n$ are the sizes of the arrays $firstList$ and $secondList$, respectively. \ No newline at end of file diff --git a/articles/max-consecutive-ones-iii.md b/articles/max-consecutive-ones-iii.md new file mode 100644 index 000000000..3e990f84b --- /dev/null +++ b/articles/max-consecutive-ones-iii.md @@ -0,0 +1,355 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def longestOnes(self, nums: List[int], k: int) -> int: + res = 0 + for l in range(len(nums)): + cnt, r = 0, l + while r < len(nums): + if nums[r] == 0: + if cnt == k: + break + cnt += 1 + r += 1 + res = max(res, r - l) + return res +``` + +```java +public class Solution { + public int longestOnes(int[] nums, int k) { + int res = 0; + for (int l = 0; l < nums.length; l++) { + int cnt = 0, r = l; + while (r < nums.length) { + if (nums[r] == 0) { + if (cnt == k) break; + cnt++; + } + r++; + } + res = Math.max(res, r - l); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestOnes(vector& nums, int k) { + int res = 0; + for (int l = 0; l < nums.size(); l++) { + int cnt = 0, r = l; + while (r < nums.size()) { + if (nums[r] == 0) { + if (cnt == k) break; + cnt++; + } + r++; + } + res = max(res, r - l); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + longestOnes(nums, k) { + let res = 0; + for (let l = 0; l < nums.length; l++) { + let cnt = 0, r = l; + while (r < nums.length) { + if (nums[r] === 0) { + if (cnt === k) break; + cnt++; + } + r++; + } + res = Math.max(res, r - l); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestOnes(int[] nums, int k) { + int res = 0; + for (int l = 0; l < nums.Length; l++) { + int cnt = 0, r = l; + while (r < nums.Length) { + if (nums[r] == 0) { + if (cnt == k) break; + cnt++; + } + r++; + } + res = Math.Max(res, r - l); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + Prefix Sum + +::tabs-start + +```python +class Solution: + def longestOnes(self, nums: List[int], k: int) -> int: + prefix = [0] + for num in nums: + prefix.append(prefix[-1] + (1 if num == 0 else 0)) + + res = 0 + for l in range(len(nums)): + low, high = l, len(nums) + while low < high: + mid = (low + high) // 2 + if prefix[mid + 1] - prefix[l] <= k: + low = mid + 1 + else: + high = mid + res = max(res, low - l) + return res +``` + +```java +public class Solution { + public int longestOnes(int[] nums, int k) { + int[] prefix = new int[nums.length + 1]; + for (int i = 0; i < nums.length; i++) { + prefix[i + 1] = prefix[i] + (nums[i] == 0 ? 1 : 0); + } + + int res = 0; + for (int l = 0; l < nums.length; l++) { + int low = l, high = nums.length; + while (low < high) { + int mid = (low + high) / 2; + if (prefix[mid + 1] - prefix[l] <= k) { + low = mid + 1; + } else { + high = mid; + } + } + res = Math.max(res, low - l); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestOnes(vector& nums, int k) { + vector prefix(nums.size() + 1, 0); + for (int i = 0; i < nums.size(); ++i) { + prefix[i + 1] = prefix[i] + (nums[i] == 0 ? 1 : 0); + } + + int res = 0; + for (int l = 0; l < nums.size(); ++l) { + int low = l, high = nums.size(); + while (low < high) { + int mid = (low + high) / 2; + if (prefix[mid + 1] - prefix[l] <= k) { + low = mid + 1; + } else { + high = mid; + } + } + res = max(res, low - l); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + longestOnes(nums, k) { + const prefix = [0]; + for (let i = 0; i < nums.length; i++) { + prefix.push(prefix[prefix.length - 1] + (nums[i] === 0 ? 1 : 0)); + } + + let res = 0; + for (let l = 0; l < nums.length; l++) { + let low = l, high = nums.length; + while (low < high) { + let mid = Math.floor((low + high) / 2); + if (prefix[mid + 1] - prefix[l] <= k) { + low = mid + 1; + } else { + high = mid; + } + } + res = Math.max(res, low - l); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestOnes(int[] nums, int k) { + int[] prefix = new int[nums.Length + 1]; + for (int i = 0; i < nums.Length; i++) { + prefix[i + 1] = prefix[i] + (nums[i] == 0 ? 1 : 0); + } + + int res = 0; + for (int l = 0; l < nums.Length; l++) { + int low = l, high = nums.Length; + while (low < high) { + int mid = (low + high) / 2; + if (prefix[mid + 1] - prefix[l] <= k) { + low = mid + 1; + } else { + high = mid; + } + } + res = Math.Max(res, low - l); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def longestOnes(self, nums: List[int], k: int) -> int: + l = res = 0 + for r in range(len(nums)): + k -= (1 if nums[r] == 0 else 0) + while k < 0: + k += (1 if nums[l] == 0 else 0) + l += 1 + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int longestOnes(int[] nums, int k) { + int l = 0, res = 0; + for (int r = 0; r < nums.length; r++) { + k -= (nums[r] == 0 ? 1 : 0); + while (k < 0) { + k += (nums[l] == 0 ? 1 : 0); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestOnes(vector& nums, int k) { + int l = 0, res = 0; + for (int r = 0; r < nums.size(); ++r) { + k -= (nums[r] == 0 ? 1 : 0); + while (k < 0) { + k += (nums[l] == 0 ? 1 : 0); + ++l; + } + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + longestOnes(nums, k) { + let l = 0, res = 0; + for (let r = 0; r < nums.length; r++) { + k -= (nums[r] === 0 ? 1 : 0); + while (k < 0) { + k += (nums[l] === 0 ? 1 : 0); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestOnes(int[] nums, int k) { + int l = 0, res = 0; + for (int r = 0; r < nums.Length; r++) { + k -= (nums[r] == 0 ? 1 : 0); + while (k < 0) { + k += (nums[l] == 0 ? 1 : 0); + l++; + } + res = Math.Max(res, r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-number-of-increments-on-subarrays-to-form-a-target-array.md b/articles/minimum-number-of-increments-on-subarrays-to-form-a-target-array.md new file mode 100644 index 000000000..2fb5136f9 --- /dev/null +++ b/articles/minimum-number-of-increments-on-subarrays-to-form-a-target-array.md @@ -0,0 +1,559 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def minNumberOperations(self, target: List[int]) -> int: + def rec(l, r, h): + if l > r: + return 0 + + minIdx = l + for i in range(l + 1, r + 1): + if target[i] < target[minIdx]: + minIdx = i + + res = target[minIdx] - h + return res + rec(l, minIdx - 1, target[minIdx]) + rec(minIdx + 1, r, target[minIdx]) + + return rec(0, len(target) - 1, 0) +``` + +```java +public class Solution { + public int minNumberOperations(int[] target) { + return rec(target, 0, target.length - 1, 0); + } + + private int rec(int[] target, int l, int r, int h) { + if (l > r) return 0; + + int minIdx = l; + for (int i = l + 1; i <= r; i++) { + if (target[i] < target[minIdx]) { + minIdx = i; + } + } + + int res = target[minIdx] - h; + res += rec(target, l, minIdx - 1, target[minIdx]); + res += rec(target, minIdx + 1, r, target[minIdx]); + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minNumberOperations(vector& target) { + return rec(target, 0, target.size() - 1, 0); + } + +private: + int rec(vector& target, int l, int r, int h) { + if (l > r) return 0; + + int minIdx = l; + for (int i = l + 1; i <= r; i++) { + if (target[i] < target[minIdx]) { + minIdx = i; + } + } + + int res = target[minIdx] - h; + res += rec(target, l, minIdx - 1, target[minIdx]); + res += rec(target, minIdx + 1, r, target[minIdx]); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} target + * @return {number} + */ + minNumberOperations(target) { + const rec = (l, r, h) => { + if (l > r) return 0; + + let minIdx = l; + for (let i = l + 1; i <= r; i++) { + if (target[i] < target[minIdx]) { + minIdx = i; + } + } + + let res = target[minIdx] - h; + res += rec(l, minIdx - 1, target[minIdx]); + res += rec(minIdx + 1, r, target[minIdx]); + + return res; + }; + + return rec(0, target.length - 1, 0); + } +} +``` + +```csharp +public class Solution { + public int MinNumberOperations(int[] target) { + return Rec(target, 0, target.Length - 1, 0); + } + + private int Rec(int[] target, int l, int r, int h) { + if (l > r) return 0; + + int minIdx = l; + for (int i = l + 1; i <= r; i++) { + if (target[i] < target[minIdx]) { + minIdx = i; + } + } + + int res = target[minIdx] - h; + res += Rec(target, l, minIdx - 1, target[minIdx]); + res += Rec(target, minIdx + 1, r, target[minIdx]); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Segment Tree + +::tabs-start + +```python +INF = float('inf') + +class SegmentTree: + def __init__(self, A): + self.A = A[:] + self.n = len(A) + while (self.n & (self.n - 1)) != 0: + self.A.append(INF) + self.n += 1 + + self.tree = [0] * (2 * self.n) + self.build() + + def build(self): + for i in range(self.n): + self.tree[self.n + i] = i + for j in range(self.n - 1, 0, -1): + a = self.tree[j << 1] + b = self.tree[(j << 1) | 1] + self.tree[j] = a if self.A[a] <= self.A[b] else b + + def update(self, i, val): + self.A[i] = val + j = (self.n + i) >> 1 + while j >= 1: + a = self.tree[j << 1] + b = self.tree[(j << 1) | 1] + self.tree[j] = a if self.A[a] <= self.A[b] else b + j >>= 1 + + def query(self, ql, qh): + return self._query(1, 0, self.n - 1, ql, qh) + + def _query(self, node, l, h, ql, qh): + if ql > h or qh < l: + return -1 + if l >= ql and h <= qh: + return self.tree[node] + mid = (l + h) >> 1 + a = self._query(node << 1, l, mid, ql, qh) + b = self._query((node << 1) | 1, mid + 1, h, ql, qh) + + if a == -1: return b + if b == -1: return a + return a if self.A[a] <= self.A[b] else b + +class Solution: + def minNumberOperations(self, target: List[int]) -> int: + seg = SegmentTree(target) + stack = [(0, len(target) - 1, 0)] + res = 0 + + while stack: + l, r, h = stack.pop() + if l > r: + continue + minIdx = seg.query(l, r) + res += target[minIdx] - h + stack.append((l, minIdx - 1, target[minIdx])) + stack.append((minIdx + 1, r, target[minIdx])) + + return res +``` + +```java +class SegmentTree { + int[] A; + int[] tree; + int n; + final int INF = Integer.MAX_VALUE; + + SegmentTree(int[] arr) { + int len = arr.length; + int pow2 = 1; + while (pow2 < len) pow2 <<= 1; + n = pow2; + + A = new int[n]; + System.arraycopy(arr, 0, A, 0, arr.length); + for (int i = arr.length; i < n; i++) A[i] = INF; + + tree = new int[2 * n]; + build(); + } + + void build() { + for (int i = 0; i < n; i++) { + tree[n + i] = i; + } + for (int i = n - 1; i > 0; i--) { + int a = tree[i << 1], b = tree[(i << 1) | 1]; + tree[i] = A[a] <= A[b] ? a : b; + } + } + + int query(int ql, int qh) { + return _query(1, 0, n - 1, ql, qh); + } + + int _query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return -1; + if (ql <= l && h <= qh) return tree[node]; + int mid = (l + h) >> 1; + int left = _query(node << 1, l, mid, ql, qh); + int right = _query((node << 1) | 1, mid + 1, h, ql, qh); + if (left == -1) return right; + if (right == -1) return left; + return A[left] <= A[right] ? left : right; + } +} + +public class Solution { + public int minNumberOperations(int[] target) { + SegmentTree seg = new SegmentTree(target); + return rec(0, target.length - 1, 0, target, seg); + } + + private int rec(int l, int r, int h, int[] target, SegmentTree seg) { + if (l > r) return 0; + int minIdx = seg.query(l, r); + int res = target[minIdx] - h; + res += rec(l, minIdx - 1, target[minIdx], target, seg); + res += rec(minIdx + 1, r, target[minIdx], target, seg); + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector A, tree; + const int INF = INT_MAX; + + SegmentTree(vector& arr) { + A = arr; + n = arr.size(); + while (__builtin_popcount(n) != 1) { + A.push_back(INF); + n++; + } + tree.resize(2 * n); + build(); + } + + void build() { + for (int i = 0; i < n; ++i) { + tree[n + i] = i; + } + for (int j = n - 1; j >= 1; --j) { + int a = tree[j << 1], b = tree[(j << 1) | 1]; + tree[j] = A[a] <= A[b] ? a : b; + } + } + + int query(int ql, int qh) { + return _query(1, 0, n - 1, ql, qh); + } + + int _query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return -1; + if (l >= ql && h <= qh) return tree[node]; + int mid = (l + h) >> 1; + int a = _query(node << 1, l, mid, ql, qh); + int b = _query((node << 1) | 1, mid + 1, h, ql, qh); + if (a == -1) return b; + if (b == -1) return a; + return A[a] <= A[b] ? a : b; + } +}; + +class Solution { +public: + int minNumberOperations(vector& target) { + SegmentTree seg(target); + return rec(0, target.size() - 1, 0, target, seg); + } + + int rec(int l, int r, int h, vector& target, SegmentTree& seg) { + if (l > r) return 0; + int minIdx = seg.query(l, r); + int res = target[minIdx] - h; + res += rec(l, minIdx - 1, target[minIdx], target, seg); + res += rec(minIdx + 1, r, target[minIdx], target, seg); + return res; + } +}; +``` + +```javascript +class SegmentTree { + constructor(A) { + this.A = [...A]; + this.n = A.length; + this.INF = Number.POSITIVE_INFINITY; + + while ((this.n & (this.n - 1)) !== 0) { + this.A.push(this.INF); + this.n++; + } + + this.tree = Array(2 * this.n).fill(0); + this.build(); + } + + build() { + for (let i = 0; i < this.n; i++) { + this.tree[this.n + i] = i; + } + for (let j = this.n - 1; j >= 1; j--) { + let a = this.tree[j << 1]; + let b = this.tree[(j << 1) | 1]; + this.tree[j] = this.A[a] <= this.A[b] ? a : b; + } + } + + update(i, val) { + this.A[i] = val; + let j = (this.n + i) >> 1; + while (j >= 1) { + let a = this.tree[j << 1]; + let b = this.tree[(j << 1) | 1]; + this.tree[j] = this.A[a] <= this.A[b] ? a : b; + j >>= 1; + } + } + + query(ql, qh) { + return this._query(1, 0, this.n - 1, ql, qh); + } + + _query(node, l, h, ql, qh) { + if (ql > h || qh < l) return -1; + if (l >= ql && h <= qh) return this.tree[node]; + + let mid = (l + h) >> 1; + let a = this._query(node << 1, l, mid, ql, qh); + let b = this._query((node << 1) | 1, mid + 1, h, ql, qh); + + if (a === -1) return b; + if (b === -1) return a; + return this.A[a] <= this.A[b] ? a : b; + } +} + +class Solution { + /** + * @param {number[]} target + * @return {number} + */ + minNumberOperations(target) { + const seg = new SegmentTree(target); + + const rec = (l, r, h) => { + if (l > r) return 0; + + const minIdx = seg.query(l, r); + let res = target[minIdx] - h; + res += rec(l, minIdx - 1, target[minIdx]); + res += rec(minIdx + 1, r, target[minIdx]); + return res; + }; + + return rec(0, target.length - 1, 0); + } +} +``` + +```csharp +public class SegmentTree { + private int[] A; + private int[] tree; + private int n; + private const int INF = int.MaxValue; + + public SegmentTree(int[] arr) { + int len = arr.Length; + int pow2 = 1; + while (pow2 < len) pow2 <<= 1; + n = pow2; + + A = new int[n]; + Array.Copy(arr, A, arr.Length); + for (int i = arr.Length; i < n; i++) A[i] = INF; + + tree = new int[2 * n]; + Build(); + } + + private void Build() { + for (int i = 0; i < n; i++) { + tree[n + i] = i; + } + for (int i = n - 1; i > 0; i--) { + int a = tree[i << 1]; + int b = tree[(i << 1) | 1]; + tree[i] = A[a] <= A[b] ? a : b; + } + } + + public int Query(int ql, int qh) { + return Query(1, 0, n - 1, ql, qh); + } + + private int Query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return -1; + if (ql <= l && h <= qh) return tree[node]; + + int mid = (l + h) >> 1; + int left = Query(node << 1, l, mid, ql, qh); + int right = Query((node << 1) | 1, mid + 1, h, ql, qh); + + if (left == -1) return right; + if (right == -1) return left; + return A[left] <= A[right] ? left : right; + } +} + +public class Solution { + public int MinNumberOperations(int[] target) { + var seg = new SegmentTree(target); + return Rec(0, target.Length - 1, 0, target, seg); + } + + private int Rec(int l, int r, int h, int[] target, SegmentTree seg) { + if (l > r) return 0; + int minIdx = seg.Query(l, r); + int res = target[minIdx] - h; + res += Rec(l, minIdx - 1, target[minIdx], target, seg); + res += Rec(minIdx + 1, r, target[minIdx], target, seg); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def minNumberOperations(self, target: List[int]) -> int: + res = target[0] + for i in range(1, len(target)): + res += max(target[i] - target[i - 1], 0) + return res +``` + +```java +public class Solution { + public int minNumberOperations(int[] target) { + int res = target[0]; + for (int i = 1; i < target.length; i++) { + res += Math.max(target[i] - target[i - 1], 0); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minNumberOperations(vector& target) { + int res = target[0]; + for (int i = 1; i < target.size(); i++) { + res += max(target[i] - target[i - 1], 0); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} target + * @return {number} + */ + minNumberOperations(target) { + let res = target[0]; + for (let i = 1; i < target.length; i++) { + res += Math.max(target[i] - target[i - 1], 0); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MinNumberOperations(int[] target) { + int res = target[0]; + for (int i = 1; i < target.Length; i++) { + res += Math.Max(target[i] - target[i - 1], 0); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/next-greater-element-i.md b/articles/next-greater-element-i.md index b81ca977a..8cf0945de 100644 --- a/articles/next-greater-element-i.md +++ b/articles/next-greater-element-i.md @@ -87,6 +87,29 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] NextGreaterElement(int[] nums1, int[] nums2) { + int n = nums2.Length; + int[] res = new int[nums1.Length]; + + for (int i = 0; i < nums1.Length; i++) { + int nextGreater = -1; + for (int j = n - 1; j >= 0; j--) { + if (nums2[j] > nums1[i]) { + nextGreater = nums2[j]; + } else if (nums2[j] == nums1[i]) { + break; + } + } + res[i] = nextGreater; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -205,6 +228,36 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] NextGreaterElement(int[] nums1, int[] nums2) { + Dictionary nums1Idx = new Dictionary(); + for (int i = 0; i < nums1.Length; i++) { + nums1Idx[nums1[i]] = i; + } + + int[] res = new int[nums1.Length]; + Array.Fill(res, -1); + + for (int i = 0; i < nums2.Length; i++) { + if (!nums1Idx.ContainsKey(nums2[i])) { + continue; + } + + for (int j = i + 1; j < nums2.Length; j++) { + if (nums2[j] > nums2[i]) { + int idx = nums1Idx[nums2[i]]; + res[idx] = nums2[j]; + break; + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -324,6 +377,38 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] NextGreaterElement(int[] nums1, int[] nums2) { + Dictionary nums1Idx = new Dictionary(); + for (int i = 0; i < nums1.Length; i++) { + nums1Idx[nums1[i]] = i; + } + + int[] res = new int[nums1.Length]; + for (int i = 0; i < res.Length; i++) { + res[i] = -1; + } + + Stack stack = new Stack(); + foreach (int num in nums2) { + while (stack.Count > 0 && num > stack.Peek()) { + int val = stack.Pop(); + if (nums1Idx.ContainsKey(val)) { + int idx = nums1Idx[val]; + res[idx] = num; + } + } + if (nums1Idx.ContainsKey(num)) { + stack.Push(num); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/number-of-enclaves.md b/articles/number-of-enclaves.md index a5da06e58..f50616154 100644 --- a/articles/number-of-enclaves.md +++ b/articles/number-of-enclaves.md @@ -153,6 +153,45 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumEnclaves(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + bool[][] visit = new bool[ROWS][]; + for (int i = 0; i < ROWS; i++) visit[i] = new bool[COLS]; + int[][] direct = new int[][] { + new int[] { 0, 1 }, new int[] { 0, -1 }, + new int[] { 1, 0 }, new int[] { -1, 0 } + }; + + int Dfs(int r, int c) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || grid[r][c] == 0 || visit[r][c]) + return 0; + + visit[r][c] = true; + int res = 1; + foreach (var d in direct) { + res += Dfs(r + d[0], c + d[1]); + } + return res; + } + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += Dfs(r, c); + } + } + } + + return land - borderLand; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -325,6 +364,51 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumEnclaves(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int[][] direct = new int[][] { + new int[] {0, 1}, new int[] {0, -1}, + new int[] {1, 0}, new int[] {-1, 0} + }; + + bool[][] visit = new bool[ROWS][]; + for (int i = 0; i < ROWS; i++) visit[i] = new bool[COLS]; + + Queue q = new Queue(); + int land = 0, borderLand = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + q.Enqueue(new int[] { r, c }); + visit[r][c] = true; + } + } + } + + while (q.Count > 0) { + var cell = q.Dequeue(); + int r = cell[0], c = cell[1]; + borderLand++; + + foreach (var d in direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS && + grid[nr][nc] == 1 && !visit[nr][nc]) { + q.Enqueue(new int[] { nr, nc }); + visit[nr][nc] = true; + } + } + } + + return land - borderLand; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -597,6 +681,71 @@ class Solution { } ``` +```csharp +public class DSU { + public 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 bool 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 NumEnclaves(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int N = ROWS * COLS; + DSU dsu = new DSU(N); + int[] directions = new int[] { 0, 1, 0, -1, 0 }; + int land = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) continue; + land++; + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] == 1) { + dsu.Union(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.Union(N, r * COLS + c); + } + } + } + } + + int borderLand = dsu.Size[dsu.Find(N)]; + return land - borderLand + 1; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/number-of-provinces.md b/articles/number-of-provinces.md new file mode 100644 index 000000000..9cd1d8d74 --- /dev/null +++ b/articles/number-of-provinces.md @@ -0,0 +1,739 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findCircleNum(self, isConnected: List[List[int]]) -> int: + n = len(isConnected) + res = 0 + visited = [False] * n + + def dfs(node): + visited[node] = True + for nei in range(n): + if isConnected[node][nei] and not visited[nei]: + dfs(nei) + + for i in range(n): + if not visited[i]: + dfs(i) + res += 1 + + return res +``` + +```java +public class Solution { + public int findCircleNum(int[][] isConnected) { + int n = isConnected.length; + boolean[] visited = new boolean[n]; + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + dfs(i, isConnected, visited, n); + res++; + } + } + return res; + } + + private void dfs(int node, int[][] isConnected, boolean[] visited, int n) { + visited[node] = true; + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] == 1 && !visited[nei]) { + dfs(nei, isConnected, visited, n); + } + } + } +} +``` + +```cpp +class Solution { +public: + int findCircleNum(vector>& isConnected) { + int n = isConnected.size(); + vector visited(n, false); + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + dfs(i, isConnected, visited, n); + res++; + } + } + return res; + } + + void dfs(int node, vector>& isConnected, vector& visited, int n) { + visited[node] = true; + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] == 1 && !visited[nei]) { + dfs(nei, isConnected, visited, n); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} isConnected + * @return {number} + */ + findCircleNum(isConnected) { + const n = isConnected.length; + const visited = new Array(n).fill(false); + let res = 0; + + const dfs = (node) => { + visited[node] = true; + for (let nei = 0; nei < n; nei++) { + if (isConnected[node][nei] === 1 && !visited[nei]) { + dfs(nei); + } + } + }; + + for (let i = 0; i < n; i++) { + if (!visited[i]) { + dfs(i); + res++; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int FindCircleNum(int[][] isConnected) { + int n = isConnected.Length; + bool[] visited = new bool[n]; + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + Dfs(i, isConnected, visited, n); + res++; + } + } + return res; + } + + private void Dfs(int node, int[][] isConnected, bool[] visited, int n) { + visited[node] = true; + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] == 1 && !visited[nei]) { + Dfs(nei, isConnected, visited, n); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Modifying Input) + +::tabs-start + +```python +class Solution: + def findCircleNum(self, isConnected: List[List[int]]) -> int: + n = len(isConnected) + res = 0 + + def dfs(node): + isConnected[node][node] = 0 + for nei in range(n): + if node != nei and isConnected[node][nei] and isConnected[nei][nei]: + dfs(nei) + + for i in range(n): + if isConnected[i][i]: + dfs(i) + res += 1 + + return res +``` + +```java +public class Solution { + public int findCircleNum(int[][] isConnected) { + int n = isConnected.length; + int res = 0; + + for (int i = 0; i < n; i++) { + if (isConnected[i][i] == 1) { + dfs(i, isConnected, n); + res++; + } + } + return res; + } + + private void dfs(int node, int[][] isConnected, int n) { + isConnected[node][node] = 0; + for (int nei = 0; nei < n; nei++) { + if (node != nei && isConnected[node][nei] == 1 && isConnected[nei][nei] == 1) { + dfs(nei, isConnected, n); + } + } + } +} +``` + +```cpp +class Solution { +public: + int findCircleNum(vector>& isConnected) { + int n = isConnected.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + if (isConnected[i][i] == 1) { + dfs(i, isConnected, n); + res++; + } + } + return res; + } + + void dfs(int node, vector>& isConnected, int n) { + isConnected[node][node] = 0; + for (int nei = 0; nei < n; nei++) { + if (node != nei && isConnected[node][nei] == 1 && isConnected[nei][nei] == 1) { + dfs(nei, isConnected, n); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} isConnected + * @return {number} + */ + findCircleNum(isConnected) { + const n = isConnected.length; + let res = 0; + + const dfs = (node) => { + isConnected[node][node] = 0; + for (let nei = 0; nei < n; nei++) { + if (node !== nei && isConnected[node][nei] === 1 && isConnected[nei][nei] === 1) { + dfs(nei); + } + } + }; + + for (let i = 0; i < n; i++) { + if (isConnected[i][i] === 1) { + dfs(i); + res++; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int FindCircleNum(int[][] isConnected) { + int n = isConnected.Length; + int res = 0; + + for (int i = 0; i < n; i++) { + if (isConnected[i][i] == 1) { + Dfs(i, isConnected, n); + res++; + } + } + + return res; + } + + private void Dfs(int node, int[][] isConnected, int n) { + isConnected[node][node] = 0; + for (int nei = 0; nei < n; nei++) { + if (node != nei && isConnected[node][nei] == 1 && isConnected[nei][nei] == 1) { + Dfs(nei, isConnected, n); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def findCircleNum(self, isConnected: List[List[int]]) -> int: + n = len(isConnected) + visited = [False] * n + res = 0 + q = deque() + + for i in range(n): + if not visited[i]: + res += 1 + visited[i] = True + q.append(i) + while q: + node = q.popleft() + for nei in range(n): + if isConnected[node][nei] and not visited[nei]: + visited[nei] = True + q.append(nei) + + return res +``` + +```java +public class Solution { + public int findCircleNum(int[][] isConnected) { + int n = isConnected.length; + boolean[] visited = new boolean[n]; + Queue q = new LinkedList<>(); + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + res++; + visited[i] = true; + q.add(i); + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] == 1 && !visited[nei]) { + visited[nei] = true; + q.add(nei); + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findCircleNum(vector>& isConnected) { + int n = isConnected.size(); + vector visited(n, false); + queue q; + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + res++; + visited[i] = true; + q.push(i); + while (!q.empty()) { + int node = q.front(); q.pop(); + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] && !visited[nei]) { + visited[nei] = true; + q.push(nei); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} isConnected + * @return {number} + */ + findCircleNum(isConnected) { + const n = isConnected.length; + const visited = new Array(n).fill(false); + const q = new Queue(); + let res = 0; + + for (let i = 0; i < n; i++) { + if (!visited[i]) { + res++; + visited[i] = true; + q.enqueue(i); + while (!q.isEmpty()) { + const node = q.dequeue(); + for (let nei = 0; nei < n; nei++) { + if (isConnected[node][nei] && !visited[nei]) { + visited[nei] = true; + q.enqueue(nei); + } + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int FindCircleNum(int[][] isConnected) { + int n = isConnected.Length; + bool[] visited = new bool[n]; + Queue q = new Queue(); + int res = 0; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + res++; + visited[i] = true; + q.Enqueue(i); + while (q.Count > 0) { + int node = q.Dequeue(); + for (int nei = 0; nei < n; nei++) { + if (isConnected[node][nei] == 1 && !visited[nei]) { + visited[nei] = true; + q.Enqueue(nei); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + self.components = 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 = self.find(u) + pv = self.find(v) + if pu == pv: + return False + + self.components -= 1 + 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 + + def numOfComps(self): + return self.components + +class Solution: + def findCircleNum(self, isConnected: List[List[int]]) -> int: + n = len(isConnected) + dsu = DSU(n) + + for i in range(n): + for j in range(n): + if isConnected[i][j]: + dsu.union(i, j) + + return dsu.numOfComps() +``` + +```java +class DSU { + int[] Parent, Size; + int components; + + public DSU(int n) { + Parent = new int[n]; + Size = new int[n]; + components = n; + 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; + + components--; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public int numOfComps() { + return components; + } +} + +public class Solution { + public int findCircleNum(int[][] isConnected) { + int n = isConnected.length; + DSU dsu = new DSU(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (isConnected[i][j] == 1) { + dsu.union(i, j); + } + } + } + return dsu.numOfComps(); + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + int components; + + DSU(int n) { + Parent.resize(n); + Size.assign(n, 1); + components = n; + 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; + + components--; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + int numOfComps() { + return components; + } +}; + +class Solution { +public: + int findCircleNum(vector>& isConnected) { + int n = isConnected.size(); + DSU dsu(n); + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + if (isConnected[i][j]) + dsu.unionSet(i, j); + return dsu.numOfComps(); + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array(n + 1).fill(0).map((_, i) => i); + this.Size = Array(n + 1).fill(1); + this.components = n; + } + + /** + * @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; + + this.components--; + 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} u + * @param {number} v + * @return {boolean} + */ + numOfComps() { + return this.components; + } +} + +class Solution { + /** + * @param {number[][]} isConnected + * @return {number} + */ + findCircleNum(isConnected) { + const n = isConnected.length; + const dsu = new DSU(n); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (isConnected[i][j]) { + dsu.union(i, j); + } + } + } + return dsu.numOfComps(); + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + private int components; + + public DSU(int n) { + Parent = new int[n]; + Size = new int[n]; + components = n; + 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 bool Union(int u, int v) { + int pu = Find(u), pv = Find(v); + if (pu == pv) return false; + + components--; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public int NumOfComps() { + return components; + } +} + +public class Solution { + public int FindCircleNum(int[][] isConnected) { + int n = isConnected.Length; + var dsu = new DSU(n); + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + if (isConnected[i][j] == 1) + dsu.Union(i, j); + return dsu.NumOfComps(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/random-pick-with-weight.md b/articles/random-pick-with-weight.md new file mode 100644 index 000000000..c9bd815a2 --- /dev/null +++ b/articles/random-pick-with-weight.md @@ -0,0 +1,345 @@ +## 1. Prefix Sum + Linear Search + +::tabs-start + +```python +class Solution: + + def __init__(self, w: List[int]): + self.w = w + self.total = sum(w) + + def pickIndex(self) -> int: + target = self.total * random.random() + curSum = 0 + for i in range(len(self.w)): + curSum += self.w[i] + if curSum > target: + return i + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(w) +# param_1 = obj.pickIndex() +``` + +```java +public class Solution { + int[] w; + int total; + + public Solution(int[] w) { + this.w = w; + for (int weight : w) { + total += weight; + } + } + + public int pickIndex() { + double target = total * Math.random(); + int curSum = 0; + for (int i = 0; i < w.length; i++) { + curSum += w[i]; + if (curSum > target) { + return i; + } + } + return -1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(w); + * int param_1 = obj.pickIndex(); + */ +``` + +```cpp +class Solution { +public: + vector w; + int total = 0; + + Solution(vector& w) { + this->w = w; + for (int weight : w) { + total += weight; + } + } + + int pickIndex() { + double target = total * ((double) rand() / RAND_MAX); + int curSum = 0; + for (int i = 0; i < w.size(); i++) { + curSum += w[i]; + if (curSum > target) { + return i; + } + } + return -1; + } +}; + + +/** + * Your Solution object will be instantiated and called as such: + * Solution* obj = new Solution(w); + * int param_1 = obj->pickIndex(); + */ +``` + +```javascript +class Solution { + /** + * @param {number[]} w + */ + constructor(w) { + this.w = w; + this.total = w.reduce((a, b) => a + b, 0); + } + + /** + * @return {number} + */ + pickIndex() { + let target = this.total * Math.random(); + let curSum = 0; + for (let i = 0; i < this.w.length; i++) { + curSum += this.w[i]; + if (curSum > target) { + return i; + } + } + return -1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * var obj = new Solution(w) + * var param_1 = obj.pickIndex() + */ +``` + +```csharp +public class Solution { + private int[] w; + private int total; + + public Solution(int[] w) { + this.w = w; + foreach (int weight in w) { + total += weight; + } + } + + public int PickIndex() { + double target = total * new Random().NextDouble(); + int curSum = 0; + for (int i = 0; i < w.Length; i++) { + curSum += w[i]; + if (curSum > target) { + return i; + } + } + return -1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(w); + * int param_1 = obj.PickIndex(); + */ +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for initializing and $O(n)$ for each $pickIndex()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + + def __init__(self, w: List[int]): + self.prefix = [0] + for wgt in w: + self.prefix.append(self.prefix[-1] + wgt) + + def pickIndex(self) -> int: + target = self.prefix[-1] * random.random() + l, r = 1, len(self.prefix) + while l < r: + mid = (l + r) >> 1 + if self.prefix[mid] <= target: + l = mid + 1 + else: + r = mid + return l - 1 + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(w) +# param_1 = obj.pickIndex() +``` + +```java +public class Solution { + private int[] prefix; + + public Solution(int[] w) { + prefix = new int[w.length + 1]; + for (int i = 0; i < w.length; i++) { + prefix[i + 1] = prefix[i] + w[i]; + } + } + + public int pickIndex() { + double target = prefix[prefix.length - 1] * Math.random(); + int l = 1, r = prefix.length; + while (l < r) { + int mid = (l + r) >> 1; + if (prefix[mid] <= target) { + l = mid + 1; + } else { + r = mid; + } + } + return l - 1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(w); + * int param_1 = obj.pickIndex(); + */ +``` + +```cpp +class Solution { +public: + vector prefix; + + Solution(vector& w) { + prefix.push_back(0); + for (int wgt : w) { + prefix.push_back(prefix.back() + wgt); + } + } + + int pickIndex() { + double target = prefix.back() * ((double) rand() / RAND_MAX); + int l = 1, r = prefix.size(); + while (l < r) { + int mid = (l + r) >> 1; + if (prefix[mid] <= target) { + l = mid + 1; + } else { + r = mid; + } + } + return l - 1; + } +}; + + +/** + * Your Solution object will be instantiated and called as such: + * Solution* obj = new Solution(w); + * int param_1 = obj->pickIndex(); + */ +``` + +```javascript +class Solution { + /** + * @param {number[]} w + */ + constructor(w) { + this.prefix = [0]; + for (let i = 0; i < w.length; i++) { + this.prefix.push(this.prefix[this.prefix.length - 1] + w[i]); + } + } + + /** + * @return {number} + */ + pickIndex() { + const total = this.prefix[this.prefix.length - 1]; + const target = total * Math.random(); + let l = 1, r = this.prefix.length; + while (l < r) { + const mid = (l + r) >> 1; + if (this.prefix[mid] <= target) { + l = mid + 1; + } else { + r = mid; + } + } + return l - 1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * var obj = new Solution(w) + * var param_1 = obj.pickIndex() + */ +``` + +```csharp +public class Solution { + private List prefix; + + public Solution(int[] w) { + prefix = new List { 0 }; + foreach (int wgt in w) { + prefix.Add(prefix[^1] + wgt); + } + } + + public int PickIndex() { + double target = prefix[^1] * new Random().NextDouble(); + int l = 1, r = prefix.Count; + while (l < r) { + int mid = (l + r) >> 1; + if (prefix[mid] <= target) { + l = mid + 1; + } else { + r = mid; + } + } + return l - 1; + } +} + + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(w); + * int param_1 = obj.PickIndex(); + */ +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for initializing and $O(\log n)$ for each $pickIndex()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/ransom-note.md b/articles/ransom-note.md new file mode 100644 index 000000000..d14efd748 --- /dev/null +++ b/articles/ransom-note.md @@ -0,0 +1,319 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def canConstruct(self, ransomNote: str, magazine: str) -> bool: + magazine = list(magazine) + + for c in ransomNote: + if c not in magazine: + return False + else: + magazine.remove(c) + + return True +``` + +```java +public class Solution { + public boolean canConstruct(String ransomNote, String magazine) { + List mag = new ArrayList<>(); + for (char c : magazine.toCharArray()) { + mag.add(c); + } + + for (char c : ransomNote.toCharArray()) { + if (!mag.contains(c)) return false; + mag.remove((Character) c); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canConstruct(string ransomNote, string magazine) { + vector mag(magazine.begin(), magazine.end()); + + for (char c : ransomNote) { + auto it = find(mag.begin(), mag.end(), c); + if (it == mag.end()) return false; + mag.erase(it); + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ransomNote + * @param {string} magazine + * @return {boolean} + */ + canConstruct(ransomNote, magazine) { + let mag = magazine.split(""); + + for (let c of ransomNote) { + let idx = mag.indexOf(c); + if (idx === -1) return false; + mag.splice(idx, 1); + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CanConstruct(string ransomNote, string magazine) { + List mag = new List(magazine); + + foreach (char c in ransomNote) { + if (!mag.Contains(c)) return false; + mag.Remove(c); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ and $n$ are the lengths of the strings $ransomNote$ and $magazine$, respectively. + +--- + +## 2. Count Frequency + +::tabs-start + +```python +class Solution: + def canConstruct(self, ransomNote: str, magazine: str) -> bool: + countR = Counter(ransomNote) + countM = Counter(magazine) + + for c in countR: + if countM[c] < countR[c]: + return False + + return True +``` + +```java +public class Solution { + public boolean canConstruct(String ransomNote, String magazine) { + int[] countR = new int[26]; + int[] countM = new int[26]; + + for (char c : ransomNote.toCharArray()) { + countR[c - 'a']++; + } + + for (char c : magazine.toCharArray()) { + countM[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + if (countM[i] < countR[i]) return false; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canConstruct(string ransomNote, string magazine) { + int countR[26] = {}; + int countM[26] = {}; + + for (char c : ransomNote) { + countR[c - 'a']++; + } + + for (char c : magazine) { + countM[c - 'a']++; + } + + for (int i = 0; i < 26; ++i) { + if (countM[i] < countR[i]) return false; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ransomNote + * @param {string} magazine + * @return {boolean} + */ + canConstruct(ransomNote, magazine) { + const countR = Array(26).fill(0); + const countM = Array(26).fill(0); + + for (const c of ransomNote) { + countR[c.charCodeAt(0) - 97]++; + } + + for (const c of magazine) { + countM[c.charCodeAt(0) - 97]++; + } + + for (let i = 0; i < 26; i++) { + if (countM[i] < countR[i]) return false; + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CanConstruct(string ransomNote, string magazine) { + int[] countR = new int[26]; + int[] countM = new int[26]; + + foreach (char c in ransomNote) { + countR[c - 'a']++; + } + + foreach (char c in magazine) { + countM[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + if (countM[i] < countR[i]) return false; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $m$ and $n$ are the lengths of the strings $ransomNote$ and $magazine$, respectively. + +--- + +## 3. Count Frequency (Optimal) + +::tabs-start + +```python +class Solution: + def canConstruct(self, ransomNote: str, magazine: str) -> bool: + count = [0] * 26 + for c in magazine: + count[ord(c) - ord('a')] += 1 + + for c in ransomNote: + count[ord(c) - ord('a')] -= 1 + if count[ord(c) - ord('a')] < 0: + return False + + return True +``` + +```java +public class Solution { + public boolean canConstruct(String ransomNote, String magazine) { + int[] count = new int[26]; + for (char c : magazine.toCharArray()) { + count[c - 'a']++; + } + for (char c : ransomNote.toCharArray()) { + if (--count[c - 'a'] < 0) return false; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canConstruct(string ransomNote, string magazine) { + int count[26] = {}; + for (char c : magazine) { + count[c - 'a']++; + } + for (char c : ransomNote) { + if (--count[c - 'a'] < 0) return false; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ransomNote + * @param {string} magazine + * @return {boolean} + */ + canConstruct(ransomNote, magazine) { + const count = Array(26).fill(0); + for (const c of magazine) { + count[c.charCodeAt(0) - 97]++; + } + for (const c of ransomNote) { + if (--count[c.charCodeAt(0) - 97] < 0) return false; + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool CanConstruct(string ransomNote, string magazine) { + int[] count = new int[26]; + foreach (char c in magazine) { + count[c - 'a']++; + } + foreach (char c in ransomNote) { + if (--count[c - 'a'] < 0) return false; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $m$ and $n$ are the lengths of the strings $ransomNote$ and $magazine$, respectively. \ No newline at end of file diff --git a/articles/split-array-with-same-average.md b/articles/split-array-with-same-average.md new file mode 100644 index 000000000..977779ffe --- /dev/null +++ b/articles/split-array-with-same-average.md @@ -0,0 +1,720 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def splitArraySameAverage(self, nums: List[int]) -> bool: + def backtrack(i, A, B): + if i == len(nums): + if not A or not B: + return False + return sum(A) * len(B) == sum(B) * len(A) + + A.append(nums[i]) + if backtrack(i + 1, A, B): + return True + B.append(nums[i]) + A.pop() + res = backtrack(i + 1, A, B) + B.pop() + return res + + return backtrack(0, [], []) +``` + +```java +public class Solution { + public boolean splitArraySameAverage(int[] nums) { + return backtrack(nums, 0, new ArrayList<>(), new ArrayList<>()); + } + + private boolean backtrack(int[] nums, int i, List A, List B) { + if (i == nums.length) { + if (A.isEmpty() || B.isEmpty()) return false; + int sumA = A.stream().mapToInt(x -> x).sum(); + int sumB = B.stream().mapToInt(x -> x).sum(); + return sumA * B.size() == sumB * A.size(); + } + + A.add(nums[i]); + if (backtrack(nums, i + 1, A, B)) return true; + A.remove(A.size() - 1); + + B.add(nums[i]); + boolean res = backtrack(nums, i + 1, A, B); + B.remove(B.size() - 1); + + return res; + } +} +``` + +```cpp +class Solution { +public: + bool splitArraySameAverage(vector& nums) { + vector A, B; + return backtrack(nums, 0, A, B); + } + + bool backtrack(vector& nums, int i, vector& A, vector& B) { + if (i == nums.size()) { + if (A.empty() || B.empty()) return false; + int sumA = accumulate(A.begin(), A.end(), 0); + int sumB = accumulate(B.begin(), B.end(), 0); + return sumA * B.size() == sumB * A.size(); + } + + A.push_back(nums[i]); + if (backtrack(nums, i + 1, A, B)) return true; + A.pop_back(); + + B.push_back(nums[i]); + bool res = backtrack(nums, i + 1, A, B); + B.pop_back(); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + splitArraySameAverage(nums) { + const backtrack = (i, A, B) => { + if (i === nums.length) { + if (A.length === 0 || B.length === 0) return false; + const sumA = A.reduce((a, b) => a + b, 0); + const sumB = B.reduce((a, b) => a + b, 0); + return sumA * B.length === sumB * A.length; + } + + A.push(nums[i]); + if (backtrack(i + 1, A, B)) return true; + A.pop(); + + B.push(nums[i]); + const res = backtrack(i + 1, A, B); + B.pop(); + + return res; + }; + + return backtrack(0, [], []); + } +} +``` + +```csharp +public class Solution { + public bool SplitArraySameAverage(int[] nums) { + return Backtrack(nums, 0, new List(), new List()); + } + + private bool Backtrack(int[] nums, int i, List A, List B) { + if (i == nums.Length) { + if (A.Count == 0 || B.Count == 0) return false; + int sumA = A.Sum(); + int sumB = B.Sum(); + return sumA * B.Count == sumB * A.Count; + } + + A.Add(nums[i]); + if (Backtrack(nums, i + 1, A, B)) return true; + A.RemoveAt(A.Count - 1); + + B.Add(nums[i]); + bool res = Backtrack(nums, i + 1, A, B); + B.RemoveAt(B.Count - 1); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Memoization (Brute Force) + +::tabs-start + +```python +class Solution: + def splitArraySameAverage(self, nums: List[int]) -> bool: + total = sum(nums) + n = len(nums) + memo = {} + + def dfs(i, size, curr_sum): + if (i, size, curr_sum) in memo: + return memo[(i, size, curr_sum)] + + if size > 0 and size < n and curr_sum * (n - size) == (total - curr_sum) * size: + return True + + if i == n: + return False + + # include nums[i] in A + if dfs(i + 1, size + 1, curr_sum + nums[i]): + memo[(i, size, curr_sum)] = True + return True + + # include nums[i] in B + if dfs(i + 1, size, curr_sum): + memo[(i, size, curr_sum)] = True + return True + + memo[(i, size, curr_sum)] = False + return False + + return dfs(0, 0, 0) +``` + +```java +public class Solution { + public boolean splitArraySameAverage(int[] nums) { + int total = 0, n = nums.length; + for (int num : nums) total += num; + Map memo = new HashMap<>(); + + return dfs(0, 0, 0, nums, total, n, memo); + } + + private boolean dfs(int i, int size, int currSum, int[] nums, int total, int n, Map memo) { + String key = i + "," + size + "," + currSum; + if (memo.containsKey(key)) return memo.get(key); + + if (size > 0 && size < n && currSum * (n - size) == (total - currSum) * size) + return true; + if (i == n) return false; + + if (dfs(i + 1, size + 1, currSum + nums[i], nums, total, n, memo) || + dfs(i + 1, size, currSum, nums, total, n, memo)) { + memo.put(key, true); + return true; + } + + memo.put(key, false); + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitArraySameAverage(vector& nums) { + int total = accumulate(nums.begin(), nums.end(), 0); + int n = nums.size(); + unordered_map memo; + + function dfs = [&](int i, int size, int currSum) { + string key = to_string(i) + "," + to_string(size) + "," + to_string(currSum); + if (memo.count(key)) return memo[key]; + + if (size > 0 && size < n && currSum * (n - size) == (total - currSum) * size) + return true; + if (i == n) return false; + + if (dfs(i + 1, size + 1, currSum + nums[i]) || dfs(i + 1, size, currSum)) + return memo[key] = true; + + return memo[key] = false; + }; + + return dfs(0, 0, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + splitArraySameAverage(nums) { + const total = nums.reduce((a, b) => a + b, 0); + const n = nums.length; + const memo = new Map(); + + const dfs = (i, size, currSum) => { + const key = `${i},${size},${currSum}`; + if (memo.has(key)) return memo.get(key); + + if (size > 0 && size < n && currSum * (n - size) === (total - currSum) * size) + return true; + if (i === n) return false; + + if (dfs(i + 1, size + 1, currSum + nums[i]) || dfs(i + 1, size, currSum)) { + memo.set(key, true); + return true; + } + + memo.set(key, false); + return false; + }; + + return dfs(0, 0, 0); + } +} +``` + +```csharp +public class Solution { + public bool SplitArraySameAverage(int[] nums) { + int total = nums.Sum(), n = nums.Length; + var memo = new Dictionary(); + + bool Dfs(int i, int size, int currSum) { + string key = $"{i},{size},{currSum}"; + if (memo.ContainsKey(key)) return memo[key]; + + if (size > 0 && size < n && currSum * (n - size) == (total - currSum) * size) + return true; + if (i == n) return false; + + if (Dfs(i + 1, size + 1, currSum + nums[i]) || Dfs(i + 1, size, currSum)) { + memo[key] = true; + return true; + } + + memo[key] = false; + return false; + } + + return Dfs(0, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * s)$ +* Space complexity: $O(n ^ 2 * s)$ + +> Where $n$ is the size of the input array $nums$, and $s$ is the sum of the elements of the array. + +--- + +## 3. Memoization (Optimal) + +::tabs-start + +```python +class Solution: + def splitArraySameAverage(self, nums: List[int]) -> bool: + n, total = len(nums), sum(nums) + # len(A) = a, len(B) = b, let a <= b + # avg(A) = avg(B) + # sum(A) / a = sum(B) / b = sum(nums) / n + # sum(A) / a = avg => sum(A) = a * avg + # sum(A) = a * sum(nums) / n + # Find if any subset exists with a * sum(nums) / n + # a is in the range [1, (n//2)] + + memo = {} + def dfs(i, a, s): + if (i, a, s) in memo: + return memo[(i, a, s)] + if a == 0: + return s == 0 + if i == n or a < 0: + return False + memo[(i, a, s)] = dfs(i + 1, a, s) or dfs(i + 1, a - 1, s - nums[i]) + return memo[(i, a, s)] + + for a in range(1, n // 2 + 1): + if total * a % n == 0: + if dfs(0, a, total * a // n): + return True + + return False +``` + +```java +public class Solution { + int[] nums; + int n, total; + Map memo; + + public boolean splitArraySameAverage(int[] nums) { + this.nums = nums; + this.n = nums.length; + this.total = 0; + for (int num : nums) total += num; + this.memo = new HashMap<>(); + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + for (int a = 1; a <= n / 2; a++) { + if ((total * a) % n == 0) { + if (dfs(0, a, (total * a) / n)) return true; + } + } + return false; + } + + private boolean dfs(int i, int a, int s) { + String key = i + "," + a + "," + s; + if (memo.containsKey(key)) return memo.get(key); + if (a == 0) return s == 0; + if (i == n || a < 0 || s < 0) return false; + boolean res = dfs(i + 1, a, s) || dfs(i + 1, a - 1, s - nums[i]); + memo.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + bool splitArraySameAverage(vector& nums) { + int n = nums.size(); + int total = accumulate(nums.begin(), nums.end(), 0); + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + vector>> memo(n + 1, + vector>(n / 2 + 1, vector(total + 1, -1))); + + function dfs = [&](int i, int a, int s) -> bool { + if (a == 0) return s == 0; + if (i == n || s < 0 || a < 0) return false; + if (memo[i][a][s] != -1) return memo[i][a][s]; + + bool res = dfs(i + 1, a, s) || dfs(i + 1, a - 1, s - nums[i]); + memo[i][a][s] = res; + return res; + }; + + for (int a = 1; a <= n / 2; ++a) { + if ((total * a) % n == 0) { + int target = (total * a) / n; + if (dfs(0, a, target)) return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + splitArraySameAverage(nums) { + const n = nums.length, total = nums.reduce((a, b) => a + b, 0); + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + const memo = new Map(); + + const dfs = (i, a, s) => { + const key = `${i},${a},${s}`; + if (memo.has(key)) return memo.get(key); + if (a === 0) return s === 0; + if (i === n || a < 0) return false; + const res = dfs(i + 1, a, s) || dfs(i + 1, a - 1, s - nums[i]); + memo.set(key, res); + return res; + }; + + for (let a = 1; a <= Math.floor(n / 2); a++) { + if ((total * a) % n === 0) { + if (dfs(0, a, Math.floor((total * a) / n))) return true; + } + } + + return false; + } +} +``` + +```csharp +public class Solution { + public bool SplitArraySameAverage(int[] nums) { + int n = nums.Length; + int total = nums.Sum(); + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + int[,,] memo = new int[n + 1, n / 2 + 1, total + 1]; + for (int i = 0; i <= n; i++) + for (int j = 0; j <= n / 2; j++) + for (int k = 0; k <= total; k++) + memo[i, j, k] = -1; + + for (int a = 1; a <= n / 2; a++) { + if ((total * a) % n == 0) { + int target = (total * a) / n; + if (Dfs(0, a, target, nums, memo)) return true; + } + } + + return false; + } + + private bool Dfs(int i, int a, int s, int[] nums, int[,,] memo) { + if (a == 0) return s == 0; + if (i == nums.Length || a < 0 || s < 0) return false; + if (memo[i, a, s] != -1) return memo[i, a, s] == 1; + + bool res = Dfs(i + 1, a, s, nums, memo) || Dfs(i + 1, a - 1, s - nums[i], nums, memo); + memo[i, a, s] = res ? 1 : 0; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * s)$ +* Space complexity: $O(n ^ 2 * s)$ + +> Where $n$ is the size of the input array $nums$, and $s$ is the sum of the elements of the array. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def splitArraySameAverage(self, nums: List[int]) -> bool: + n = len(nums) + # len(A) = a, len(B) = b, let a <= b + # avg(A) = avg(B) + # sum(A) / a = sum(B) / b = sum(nums) / n + # sum(A) / a = avg => sum(A) = a * avg + # sum(A) = a * sum(nums) / n + # Find if any subset exists with a * sum(nums) / n + # a is in the range [1, (n//2)] + + total = sum(nums) + dp = [set() for _ in range(n // 2 + 1)] + + dp[0].add(0) + for num in nums: + for a in range(n // 2, 0, -1): + for prev in dp[a - 1]: + dp[a].add(prev + num) + + for a in range(1, n // 2 + 1): + if (a * total % n == 0) and (a * total // n) in dp[a]: + return True + + return False +``` + +```java +public class Solution { + public boolean splitArraySameAverage(int[] nums) { + int n = nums.length; + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + int total = 0; + for (int num : nums) total += num; + + List> dp = new ArrayList<>(); + for (int i = 0; i <= n / 2; i++) { + dp.add(new HashSet<>()); + } + dp.get(0).add(0); + + for (int num : nums) { + for (int a = n / 2; a >= 1; a--) { + for (int prev : dp.get(a - 1)) { + dp.get(a).add(prev + num); + } + } + } + + for (int a = 1; a <= n / 2; a++) { + if ((a * total) % n == 0 && dp.get(a).contains((a * total) / n)) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitArraySameAverage(vector& nums) { + int n = nums.size(); + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + int total = accumulate(nums.begin(), nums.end(), 0); + vector> dp(n / 2 + 1); + dp[0].insert(0); + + for (int num : nums) { + for (int a = n / 2; a >= 1; a--) { + for (int prev : dp[a - 1]) { + dp[a].insert(prev + num); + } + } + } + + for (int a = 1; a <= n / 2; ++a) { + if ((a * total) % n == 0 && dp[a].count((a * total) / n)) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + splitArraySameAverage(nums) { + const n = nums.length; + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + const total = nums.reduce((a, b) => a + b, 0); + const dp = Array.from({ length: Math.floor(n / 2) + 1 }, () => new Set()); + dp[0].add(0); + + for (const num of nums) { + for (let a = Math.floor(n / 2); a >= 1; a--) { + for (const prev of dp[a - 1]) { + dp[a].add(prev + num); + } + } + } + + for (let a = 1; a <= Math.floor(n / 2); a++) { + if ((a * total) % n === 0 && dp[a].has((a * total) / n)) { + return true; + } + } + + return false; + } +} +``` + +```csharp +public class Solution { + public bool SplitArraySameAverage(int[] nums) { + int n = nums.Length; + + // len(A) = a, len(B) = b, let a <= b + // avg(A) = avg(B) + // sum(A) / a = sum(B) / b = sum(nums) / n + // sum(A) / a = avg => sum(A) = a * avg + // sum(A) = a * sum(nums) / n + // Find if any subset exists with a * sum(nums) / n + // a is in the range [1, (n//2)] + + int total = nums.Sum(); + List> dp = new List>(); + for (int i = 0; i <= n / 2; i++) { + dp.Add(new HashSet()); + } + dp[0].Add(0); + + foreach (int num in nums) { + for (int a = n / 2; a >= 1; a--) { + foreach (int prev in dp[a - 1]) { + dp[a].Add(prev + num); + } + } + } + + for (int a = 1; a <= n / 2; a++) { + if ((a * total) % n == 0 && dp[a].Contains((a * total) / n)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * s)$ +* Space complexity: $O(n ^ 2 * s)$ + +> Where $n$ is the size of the input array $nums$, and $s$ is the sum of the elements of the array. \ No newline at end of file diff --git a/articles/string-compression.md b/articles/string-compression.md new file mode 100644 index 000000000..cd1f85ab7 --- /dev/null +++ b/articles/string-compression.md @@ -0,0 +1,294 @@ +## 1. Using Extra Space + +::tabs-start + +```python +class Solution: + def compress(self, chars: List[str]) -> int: + n = len(chars) + s = "" + + i = 0 + while i < n: + s += chars[i] + j = i + 1 + while j < n and chars[i] == chars[j]: + j += 1 + + if j - i > 1: + s += str(j - i) + i = j + + i = 0 + while i < len(s): + chars[i] = s[i] + i += 1 + return i +``` + +```java +public class Solution { + public int compress(char[] chars) { + int n = chars.length; + StringBuilder s = new StringBuilder(); + + int i = 0; + while (i < n) { + s.append(chars[i]); + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + s.append(String.valueOf(j - i)); + } + i = j; + } + + for (i = 0; i < s.length(); i++) { + chars[i] = s.charAt(i); + } + return s.length(); + } +} +``` + +```cpp +class Solution { +public: + int compress(vector& chars) { + int n = chars.size(); + string s = ""; + + int i = 0; + while (i < n) { + s += chars[i]; + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + s += to_string(j - i); + } + i = j; + } + + for (i = 0; i < s.size(); i++) { + chars[i] = s[i]; + } + return s.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} chars + * @return {number} + */ + compress(chars) { + const n = chars.length; + let s = ""; + + let i = 0; + while (i < n) { + s += chars[i]; + let j = i + 1; + while (j < n && chars[i] === chars[j]) { + j++; + } + + if (j - i > 1) { + s += String(j - i); + } + i = j; + } + + for (i = 0; i < s.length; i++) { + chars[i] = s[i]; + } + return s.length; + } +} +``` + +```csharp +public class Solution { + public int Compress(char[] chars) { + int n = chars.Length; + string s = ""; + + int i = 0; + while (i < n) { + s += chars[i]; + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + s += (j - i).ToString(); + } + i = j; + } + + for (i = 0; i < s.Length; i++) { + chars[i] = s[i]; + } + return s.Length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ or $O(n ^ 2)$ depending on the language. +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def compress(self, chars: List[str]) -> int: + n = len(chars) + k = i = 0 + + while i < n: + chars[k] = chars[i] + k += 1 + j = i + 1 + while j < n and chars[i] == chars[j]: + j += 1 + + if j - i > 1: + for c in str(j - i): + chars[k] = c + k += 1 + i = j + return k +``` + +```java +public class Solution { + public int compress(char[] chars) { + int n = chars.length, k = 0, i = 0; + + while (i < n) { + chars[k++] = chars[i]; + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + String cnt = String.valueOf(j - i); + for (char c : cnt.toCharArray()) { + chars[k++] = c; + } + } + i = j; + } + + return k; + } +} +``` + +```cpp +class Solution { +public: + int compress(vector& chars) { + int n = chars.size(), k = 0, i = 0; + + while (i < n) { + chars[k++] = chars[i]; + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + string cnt = to_string(j - i); + for (char c : cnt) { + chars[k++] = c; + } + } + i = j; + } + + return k; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} chars + * @return {number} + */ + compress(chars) { + let n = chars.length, k = 0, i = 0; + + while (i < n) { + chars[k++] = chars[i]; + let j = i + 1; + while (j < n && chars[i] === chars[j]) { + j++; + } + + if (j - i > 1) { + const cnt = String(j - i); + for (const c of cnt) { + chars[k++] = c; + } + } + i = j; + } + + return k; + } +} +``` + +```csharp +public class Solution { + public int Compress(char[] chars) { + int n = chars.Length, k = 0, i = 0; + + while (i < n) { + chars[k++] = chars[i]; + int j = i + 1; + while (j < n && chars[i] == chars[j]) { + j++; + } + + if (j - i > 1) { + string cnt = (j - i).ToString(); + foreach (char c in cnt) { + chars[k++] = c; + } + } + i = j; + } + + return k; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file