From 8df8b995287c73f8d98ccc45d169ae57d3e4e11c Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 3 Nov 2025 00:56:40 -0800 Subject: [PATCH 01/32] add maximum-distance-in-arrays article --- articles/maximum-distance-in-arrays.md | 420 +++++++++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 articles/maximum-distance-in-arrays.md diff --git a/articles/maximum-distance-in-arrays.md b/articles/maximum-distance-in-arrays.md new file mode 100644 index 000000000..5ef4ed27f --- /dev/null +++ b/articles/maximum-distance-in-arrays.md @@ -0,0 +1,420 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxDistance(self, arrays: list[list[int]]) -> int: + res = 0 + n = len(arrays) + for i in range(n - 1): + for j in range(len(arrays[i])): + for k in range(i + 1, n): + for l in range(len(arrays[k])): + res = max(res, abs(arrays[i][j] - arrays[k][l])) + return res +``` + +```java +class Solution { + public int maxDistance(List> arrays) { + int res = 0; + int n = arrays.size(); + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < arrays.get(i).size(); j++) { + for (int k = i + 1; k < n; k++) { + for (int l = 0; l < arrays.get(k).size(); l++) { + res = Math.max(res, Math.abs(arrays.get(i).get(j) - arrays.get(k).get(l))); + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(std::vector>& arrays) { + int res = 0; + int n = arrays.size(); + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < arrays[i].size(); j++) { + for (int k = i + 1; k < n; k++) { + for (int l = 0; l < arrays[k].size(); l++) { + res = std::max(res, std::abs(arrays[i][j] - arrays[k][l])); + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} arrays + * @return {number} + */ + maxDistance(arrays) { + let res = 0; + const n = arrays.length; + for (let i = 0; i < n - 1; i++) { + for (let j = 0; j < arrays[i].length; j++) { + for (let k = i + 1; k < n; k++) { + for (let l = 0; l < arrays[k].length; l++) { + res = Math.max(res, Math.abs(arrays[i][j] - arrays[k][l])); + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxDistance(IList> arrays) { + int res = 0; + int n = arrays.Count; + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < arrays[i].Count; j++) { + for (int k = i + 1; k < n; k++) { + for (int l = 0; l < arrays[k].Count; l++) { + res = Math.Max(res, Math.Abs(arrays[i][j] - arrays[k][l])); + } + } + } + } + return res; + } +} +``` + +```go +func maxDistance(arrays [][]int) int { + res := 0 + n := len(arrays) + for i := 0; i < n-1; i++ { + for j := 0; j < len(arrays[i]); j++ { + for k := i + 1; k < n; k++ { + for l := 0; l < len(arrays[k]); l++ { + diff := arrays[i][j] - arrays[k][l] + if diff < 0 { + diff = -diff + } + if diff > res { + res = diff + } + } + } + } + } + return res +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O((n * x)^2)$ +- Space complexity: $O(1)$ extra space used + +> Where $n$ refers to the number of arrays in $arrays$ and $x$ refers to the average number of elements in each array in $arrays$. + +--- + +## 2. Better Brute Force + +::tabs-start + +```python +class Solution: + def maxDistance(self, arrays: list[list[int]]) -> int: + res = 0 + n = len(arrays) + for i in range(n - 1): + for j in range(i + 1, n): + array1 = arrays[i] + array2 = arrays[j] + res = max(res, abs(array1[0] - array2[-1])) + res = max(res, abs(array2[0] - array1[-1])) + return res +``` + +```java +class Solution { + public int maxDistance(List> arrays) { + List array1, array2; + int res = 0; + int n = arrays.size(); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + array1 = arrays.get(i); + array2 = arrays.get(j); + res = Math.max(res, Math.abs(array1.get(0) - array2.get(array2.size() - 1))); + res = Math.max(res, Math.abs(array2.get(0) - array1.get(array1.size() - 1))); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(std::vector>& arrays) { + std::vector array1, array2; + int res = 0; + int n = arrays.size(); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + array1 = arrays[i]; + array2 = arrays[j]; + res = std::max(res, std::abs(array1[0] - array2[array2.size() - 1])); + res = std::max(res, std::abs(array2[0] - array1[array1.size() - 1])); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} arrays + * @return {number} + */ + maxDistance(arrays) { + let array1, array2; + let res = 0; + const n = arrays.length; + for (let i = 0; i < n - 1; i++) { + for (let j = i + 1; j < n; j++) { + array1 = arrays[i]; + array2 = arrays[j]; + res = Math.max(res, Math.abs(array1[0] - array2[array2.length - 1])); + res = Math.max(res, Math.abs(array2[0] - array1[array1.length - 1])); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxDistance(IList> arrays) { + IList array1, array2; + int res = 0; + int n = arrays.Count; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + array1 = arrays[i]; + array2 = arrays[j]; + res = Math.Max(res, Math.Abs(array1[0] - array2[array2.Count - 1])); + res = Math.Max(res, Math.Abs(array2[0] - array1[array1.Count - 1])); + } + } + return res; + } +} +``` + +```go +func maxDistance(arrays [][]int) int { + var array1, array2 []int + res := 0 + n := len(arrays) + for i := 0; i < n-1; i++ { + for j := i + 1; j < n; j++ { + array1 = arrays[i] + array2 = arrays[j] + res = max(res, abs(array1[0] - array2[len(array2)-1])) + res = max(res, abs(array2[0] - array1[len(array1)-1])) + } + } + return res +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n^2)$ +- Space complexity: $O(1)$ extra space used + +> Where $n$ is the number of arrays in $arrays$ + +--- + +## 3. Single Scan + +::tabs-start + +```python +class Solution: + def maxDistance(self, arrays: list[list[int]]) -> int: + res = 0 + n = len(arrays[0]) + min_val = arrays[0][0] + max_val = arrays[0][-1] + for i in range(1, len(arrays)): + n = len(arrays[i]) + res = max(res, max(abs(arrays[i][n - 1] - min_val), + abs(max_val - arrays[i][0]))) + min_val = min(min_val, arrays[i][0]) + max_val = max(max_val, arrays[i][n - 1]) + return res +``` + +```java +class Solution { + public int maxDistance(List> arrays) { + int res = 0; + int n = arrays.get(0).size(); + int min_val = arrays.get(0).get(0); + int max_val = arrays.get(0).get(arrays.get(0).size() - 1); + for (int i = 1; i < arrays.size(); i++) { + n = arrays.get(i).size(); + res = Math.max(res, Math.max(Math.abs(arrays.get(i).get(n - 1) - min_val), + Math.abs(max_val - arrays.get(i).get(0)))); + min_val = Math.min(min_val, arrays.get(i).get(0)); + max_val = Math.max(max_val, arrays.get(i).get(n - 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(std::vector>& arrays) { + int res = 0; + int n = arrays[0].size(); + int min_val = arrays[0][0]; + int max_val = arrays[0][arrays[0].size() - 1]; + for (int i = 1; i < arrays.size(); i++) { + n = arrays[i].size(); + res = std::max(res, std::max(std::abs(arrays[i][n - 1] - min_val), + std::abs(max_val - arrays[i][0]))); + min_val = std::min(min_val, arrays[i][0]); + max_val = std::max(max_val, arrays[i][n - 1]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} arrays + * @return {number} + */ + maxDistance(arrays) { + let res = 0; + let n = arrays[0].length; + let min_val = arrays[0][0]; + let max_val = arrays[0][arrays[0].length - 1]; + for (let i = 1; i < arrays.length; i++) { + n = arrays[i].length; + res = Math.max(res, Math.max(Math.abs(arrays[i][n - 1] - min_val), + Math.abs(max_val - arrays[i][0]))); + min_val = Math.min(min_val, arrays[i][0]); + max_val = Math.max(max_val, arrays[i][n - 1]); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxDistance(IList> arrays) { + int res = 0; + int n = arrays[0].Count; + int min_val = arrays[0][0]; + int max_val = arrays[0][arrays[0].Count - 1]; + for (int i = 1; i < arrays.Count; i++) { + n = arrays[i].Count; + res = Math.Max(res, Math.Max(Math.Abs(arrays[i][n - 1] - min_val), + Math.Abs(max_val - arrays[i][0]))); + min_val = Math.Min(min_val, arrays[i][0]); + max_val = Math.Max(max_val, arrays[i][n - 1]); + } + return res; + } +} +``` + +```go +func maxDistance(arrays [][]int) int { + res := 0 + n := len(arrays[0]) + min_val := arrays[0][0] + max_val := arrays[0][n-1] + for i := 1; i < len(arrays); i++ { + n = len(arrays[i]) + res = max(res, max(abs(arrays[i][n-1]-min_val), + abs(max_val-arrays[i][0]))) + min_val = min(min_val, arrays[i][0]) + max_val = max(max_val, arrays[i][n-1]) + } + return res +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ We traverse over $arrays$ of length n +- Space complexity: $O(1)$ extra space used + +> Where $n$ is the number of arrays in $arrays$ From 5540f79cb09fe2a17a0f7439d5072475123afe19 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 3 Nov 2025 01:05:30 -0800 Subject: [PATCH 02/32] fix article --- articles/maximum-distance-in-arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/maximum-distance-in-arrays.md b/articles/maximum-distance-in-arrays.md index 5ef4ed27f..a79a1782a 100644 --- a/articles/maximum-distance-in-arrays.md +++ b/articles/maximum-distance-in-arrays.md @@ -414,7 +414,7 @@ func min(a, b int) int { ### Time & Space Complexity -- Time complexity: $O(n)$ We traverse over $arrays$ of length n +- Time complexity: $O(n)$ - Space complexity: $O(1)$ extra space used > Where $n$ is the number of arrays in $arrays$ From 8c6c43ac5885985639bcbf72230b9d2680e48cf6 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 3 Nov 2025 01:28:47 -0800 Subject: [PATCH 03/32] fix csharp method signature --- articles/maximum-distance-in-arrays.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/articles/maximum-distance-in-arrays.md b/articles/maximum-distance-in-arrays.md index a79a1782a..2cea257e8 100644 --- a/articles/maximum-distance-in-arrays.md +++ b/articles/maximum-distance-in-arrays.md @@ -79,7 +79,7 @@ class Solution { ```csharp public class Solution { - public int MaxDistance(IList> arrays) { + public int MaxDistance(List> arrays) { int res = 0; int n = arrays.Count; for (int i = 0; i < n - 1; i++) { @@ -212,8 +212,8 @@ class Solution { ```csharp public class Solution { - public int MaxDistance(IList> arrays) { - IList array1, array2; + public int MaxDistance(List> arrays) { + List array1, array2; int res = 0; int n = arrays.Count; for (int i = 0; i < n - 1; i++) { @@ -355,7 +355,7 @@ class Solution { ```csharp public class Solution { - public int MaxDistance(IList> arrays) { + public int MaxDistance(List> arrays) { int res = 0; int n = arrays[0].Count; int min_val = arrays[0][0]; From a7b453d6b8788a49dfd1bc92ea86436549d6ae4f Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 3 Nov 2025 16:07:16 -0800 Subject: [PATCH 04/32] fix wiggle-sort python heap solution --- articles/wiggle-sort.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/wiggle-sort.md b/articles/wiggle-sort.md index 250815c41..d5307d391 100644 --- a/articles/wiggle-sort.md +++ b/articles/wiggle-sort.md @@ -11,13 +11,13 @@ class Solution: maxHeap = [] for num in nums: - heappush(maxHeap, -num) + heapq.heappush(maxHeap, -num) n = len(nums) for i in range(1, n, 2): - nums[i] = -heappop(maxHeap) + nums[i] = -heapq.heappop(maxHeap) for i in range(0, n, 2): - nums[i] = -heappop(maxHeap) + nums[i] = -heapq.heappop(maxHeap) ``` ```java From de5bc6760f9eb4ad39b24ca4ac47cefc0d419660 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 6 Nov 2025 23:58:40 -0800 Subject: [PATCH 05/32] add confusing-number.md article --- articles/confusing-number.md | 252 +++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 articles/confusing-number.md diff --git a/articles/confusing-number.md b/articles/confusing-number.md new file mode 100644 index 000000000..0c151f642 --- /dev/null +++ b/articles/confusing-number.md @@ -0,0 +1,252 @@ +## 1. Invert and Reverse + +::tabs-start + +```python +class Solution: + def confusingNumber(self, n: int) -> bool: + # Use 'invertMap' to invert each valid digit. + invert_map = {"0":"0", "1":"1", "8":"8", "6":"9", "9":"6"} + rotated_number = [] + + # Iterate over each digit of 'n'. + for ch in str(n): + if ch not in invert_map: + return False + + # Append the inverted digit of 'ch' to the end of 'rotated_number'. + rotated_number.append(invert_map[ch]) + + rotated_number = "".join(rotated_number) + + # Check if the reversed 'rotated_number' equals 'n'. + return int(rotated_number[::-1]) != n +``` + +```java +class Solution { + public boolean confusingNumber(int n) { + // Use 'invertMap' to invert each valid digit. + Map invertMap = new HashMap<>() {{ + put('0', '0'); + put('1', '1'); + put('6', '9'); + put('8', '8'); + put('9', '6'); + }}; + StringBuilder sb = new StringBuilder(); + + // Iterate over each digit of 'n'. + for (char ch : String.valueOf(n).toCharArray()) { + if (!invertMap.containsKey(ch)) { + return false; + } + + // Append the inverted digit of 'ch' to the end of 'rotatedNumber'. + sb.append(invertMap.get(ch)); + } + + // Check if the reversed 'rotatedNumber' equals 'n'. + sb.reverse(); + return Integer.parseInt(sb.toString()) != n; + } +} +``` + +```cpp +class Solution { +public: + bool confusingNumber(int n) { + // Use 'invertMap' to invert each valid digit. + unordered_map invertMap = {{'0','0'}, {'1','1'}, {'6','9'}, {'8','8'}, {'9','6'}}; + string rotatedNumber; + + // Iterate over each digit of 'n'. + for (auto ch : to_string(n)) { + if (invertMap.find(ch) == invertMap.end()) { + return false; + } + + // Append the inverted digit of 'ch' to the end of 'rotatedNumber'. + rotatedNumber += invertMap[ch]; + } + + // Check if the reversed 'rotatedNumber' equals 'n'. + reverse(begin(rotatedNumber), end(rotatedNumber)); + return stoi(rotatedNumber) != n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + confusingNumber(n) { + // Use 'invertMap' to invert each valid digit. + const invertMap = { + '0': '0', + '1': '1', + '6': '9', + '8': '8', + '9': '6' + }; + + let rotatedNumber = ''; + // Iterate over each digit of 'n'. + for (const ch of String(n)) { + if (!(ch in invertMap)) { + return false; + } + // Append the inverted digit of 'ch' to the end of 'rotatedNumber'. + rotatedNumber += invertMap[ch]; + } + // Check if the reversed 'rotatedNumber' equals 'n'. + rotatedNumber = rotatedNumber.split('').reverse().join(''); + return parseInt(rotatedNumber) !== n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(L)$ +- Space complexity: $O(L)$ extra space used + +> Where $L$ is the maximum number of digits $n$ can have. + +--- + +## 2. Use the remainder + +::tabs-start + +```python +class Solution: + def confusingNumber(self, n: int) -> bool: + # Use 'invert_map' to invert each valid digit. Since we don't want to modify + # 'n', we create a copy of it as 'nCopy'. + invert_map = {0:0, 1:1, 8:8, 6:9, 9:6} + invert_number = 0 + n_copy = n + + # Get every digit of 'n_copy' by taking the remainder of it to 10. + while n_copy: + res = n_copy % 10 + if res not in invert_map: + return False + + # Append the inverted digit of 'res' to the end of 'rotated_number'. + invert_number = invert_number * 10 + invert_map[res] + n_copy //= 10 + + # Check if 'rotated_number' equals 'n'. + return invert_number != n +``` + +```java +class Solution { + public boolean confusingNumber(int n) { + // Use 'invertMap' to invert each valid digit. Since we don't want to modify + // 'n', we create a copy of it as 'nCopy'. + Map invertMap = new HashMap<>() {{ + put(0, 0); + put(1, 1); + put(6, 9); + put(8, 8); + put(9, 6); + }}; + int nCopy = n, rotatedNumber = 0; + + // Get every digit of 'nCopy' by taking the remainder of it to 10. + while (nCopy > 0) { + int res = nCopy % 10; + if (!invertMap.containsKey(res)) { + return false; + } + + // Append the inverted digit of 'res' to the end of 'rotatedNumber'. + rotatedNumber = rotatedNumber * 10 + invertMap.get(res); + nCopy /= 10; + } + + // Check if 'rotatedNumber' equals 'n'. + return rotatedNumber != n; + } +} +``` + +```cpp +class Solution { +public: + bool confusingNumber(int n) { + // Use 'invertMap' to invert each valid digit. Since we don't want to modify + // 'n', we create a copy of it as 'nCopy'. + map invertMap = {{0, 0}, {1, 1}, {6, 9}, {8, 8}, {9, 6}}; + int rotatedNumber = 0, nCopy = n; + + // Get every digit of 'nCopy' by taking the remainder of it to 10. + while (nCopy > 0) { + int res = nCopy % 10; + if (invertMap.find(res) == invertMap.end()) { + return false; + } + + // Append the inverted digit of 'res' to the end of 'rotatedNumber'. + rotatedNumber = rotatedNumber * 10 + invertMap[res]; + nCopy /= 10; + } + + // Check if 'rotatedNumber' equals 'n'. + return rotatedNumber != n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + confusingNumber(n) { + // Use 'invertMap' to invert each valid digit. Since we don't want to modify + // 'n', we create a copy of it as 'nCopy'. + const invertMap = { + 0: 0, + 1: 1, + 6: 9, + 8: 8, + 9: 6 + }; + let nCopy = n; + let rotatedNumber = 0; + + // Get every digit of 'nCopy' by taking the remainder of it to 10. + while (nCopy > 0) { + let res = nCopy % 10; + if (!(res in invertMap)) { + return false; + } + // Append the inverted digit of 'res' to the end of 'rotatedNumber'. + rotatedNumber = rotatedNumber * 10 + invertMap[res]; + nCopy = Math.floor(nCopy / 10); + } + // Check if 'rotatedNumber' equals 'n'. + return rotatedNumber !== n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(L)$ +- Space complexity: $O(L)$ extra space used + +> Where $L$ is the maximum number of digits $n$ can have. From 68ca9788d911abb755730f7c1616e0a2674d4346 Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:37:08 -0800 Subject: [PATCH 06/32] Update explanation of L in complexity analysis Clarify the definition of L in terms of n. --- articles/confusing-number.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/confusing-number.md b/articles/confusing-number.md index 0c151f642..046735e31 100644 --- a/articles/confusing-number.md +++ b/articles/confusing-number.md @@ -117,7 +117,7 @@ class Solution { - Time complexity: $O(L)$ - Space complexity: $O(L)$ extra space used -> Where $L$ is the maximum number of digits $n$ can have. +> Where $L$ is the maximum number of digits $n$ can have ($L = \log_{10}(n)$). --- From 6e89d0841ef23c2d7086a6a30f32494a381e04e3 Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:38:22 -0800 Subject: [PATCH 07/32] Fix logarithm notation in complexity section Updated logarithm notation in complexity explanation. --- articles/confusing-number.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/confusing-number.md b/articles/confusing-number.md index 046735e31..d4c3b560a 100644 --- a/articles/confusing-number.md +++ b/articles/confusing-number.md @@ -117,7 +117,7 @@ class Solution { - Time complexity: $O(L)$ - Space complexity: $O(L)$ extra space used -> Where $L$ is the maximum number of digits $n$ can have ($L = \log_{10}(n)$). +> Where $L$ is the maximum number of digits $n$ can have ($L = log_{10}(n)$). --- From c0ba543effe32c7729273877ac10271e3409d589 Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:41:00 -0800 Subject: [PATCH 08/32] Fix LaTeX formatting for logarithm in documentation --- articles/confusing-number.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/confusing-number.md b/articles/confusing-number.md index d4c3b560a..494cca7a3 100644 --- a/articles/confusing-number.md +++ b/articles/confusing-number.md @@ -117,7 +117,7 @@ class Solution { - Time complexity: $O(L)$ - Space complexity: $O(L)$ extra space used -> Where $L$ is the maximum number of digits $n$ can have ($L = log_{10}(n)$). +> Where $L$ is the maximum number of digits $n$ can have ($L = \log_{10} n$). --- From 16993128a94b036d5903ca13b883ff7c2c364de9 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sun, 9 Nov 2025 01:00:28 -0800 Subject: [PATCH 09/32] add perform-string-shifts article --- articles/perform-string-shifts.md | 203 ++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 articles/perform-string-shifts.md diff --git a/articles/perform-string-shifts.md b/articles/perform-string-shifts.md new file mode 100644 index 000000000..4a8747991 --- /dev/null +++ b/articles/perform-string-shifts.md @@ -0,0 +1,203 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def stringShift(self, s: str, shift: List[List[int]]) -> str: + for direction, amount in shift: + amount %= len(s) + if direction == 0: + # Move necessary amount of characters from start to end + s = s[amount:] + s[:amount] + else: + # Move necessary amount of characters from end to start + s = s[-amount:] + s[:-amount] + return s +``` + +```java +class Solution { + public String stringShift(String s, int[][] shift) { + int len = s.length(); + + for (int[] move : shift) { + int direction = move[0]; + int amount = move[1] % len; + if (direction == 0) { + // Move necessary amount of characters from front to end + s = s.substring(amount) + s.substring(0, amount); + } else { + // Move necessary amount of characters from end to front + s = + s.substring(len - amount) + + s.substring(0, len - amount); + } + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string stringShift(string s, vector>& shift) { + int len = s.length(); + + for (auto move : shift) { + int direction = move[0]; + int amount = move[1] % len; + if (direction == 0) { + // Move necessary amount of characters from start to end + s = s.substr(amount) + s.substr(0, amount); + } else { + // Move necessary amount of characters from end to start + s = s.substr(len - amount) + + s.substr(0, len - amount); + } + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shift + * @return {string} + */ + stringShift(s, shift) { + const len = s.length; + + for (const move of shift) { + const direction = move[0]; + const amount = move[1] % len; + + if (direction === 0) { + // Move necessary amount of characters from front to end + s = s.substring(amount) + s.substring(0, amount); + } else { + // Move necessary amount of characters from end to front + s = + s.substring(len - amount) + + s.substring(0, len - amount); + } + } + + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N * L)$ +- Space complexity: $O(L)$ extra space used + +> Where $L$ is the length of the string and $N$ is the length of the `shift` array + +--- + +## 2. Compute Net Shift + +::tabs-start + +```python +class Solution: + def stringShift(self, s: str, shift: List[List[int]]) -> str: + # Count the number of left shifts. A right shift is a negative left shift. + left_shifts = 0 + for direction, amount in shift: + if direction == 1: + amount = -amount + left_shifts += amount + + # Convert back to a positive, do left shifts, and return. + left_shifts %= len(s) + s = s[left_shifts:] + s[:left_shifts] + return s +``` + +```java +class Solution { + public String stringShift(String s, int[][] shift) { + // Count the number of left shifts. A right shift is a negative left shift. + int leftShifts = 0; + + for (int[] move : shift) { + if (move[0] == 1) { + move[1] = -move[1]; + } + leftShifts += move[1]; + } + + // Convert back to a positive, do left shifts, and return. + leftShifts = Math.floorMod(leftShifts, s.length()); + s = s.substring(leftShifts) + s.substring(0, leftShifts); + return s; + } +} +``` + +```cpp +class Solution { +public: + string stringShift(string s, vector>& shift) { + // Count the number of left shifts. A right shift is a negative left shift. + int leftShifts = 0; + + for (auto& move : shift) { + if (move[0] == 1) { + move[1] = -move[1]; + } + leftShifts += move[1]; + } + + // Convert back to a positive, do left shifts, and return. + int n = s.length(); + leftShifts = ((leftShifts % n) + n) % n; + s = s.substr(leftShifts) + s.substr(0, leftShifts); + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shift + * @return {string} + */ + stringShift(s, shift) { + // Count the number of left shifts. A right shift is a negative left shift. + let leftShifts = 0; + + for (let move of shift) { + if (move[0] === 1) { + move[1] = -move[1]; + } + leftShifts += move[1]; + } + + // Convert back to a positive, do left shifts, and return. + leftShifts = ((leftShifts % s.length) + s.length) % s.length; + s = s.substring(leftShifts) + s.substring(0, leftShifts); + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N + L)$ +- Space complexity: $O(L)$ extra space used + +> Where $L$ is the length of the string and $N$ is the length of the `shift` array From 7f6735f42d7ed9355a11b58e323d33203ed46d72 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 10 Nov 2025 02:45:16 -0800 Subject: [PATCH 10/32] add one-edit-distance article --- articles/one-edit-distance.md | 138 ++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 articles/one-edit-distance.md diff --git a/articles/one-edit-distance.md b/articles/one-edit-distance.md new file mode 100644 index 000000000..86bf0b11d --- /dev/null +++ b/articles/one-edit-distance.md @@ -0,0 +1,138 @@ +## 1. One Pass Algorithm + +::tabs-start + +```python +class Solution: + def isOneEditDistance(self, s: "str", t: "str") -> "bool": + ns, nt = len(s), len(t) + + # Ensure that s is shorter than t. + if ns > nt: + return self.isOneEditDistance(t, s) + + # The strings are NOT one edit away from distance + # if the length diff is more than 1. + if nt - ns > 1: + return False + + for i in range(ns): + if s[i] != t[i]: + # If strings have the same length + if ns == nt: + return s[i + 1 :] == t[i + 1 :] + # If strings have different lengths + else: + return s[i:] == t[i + 1 :] + + # If there are no diffs in ns distance + # The strings are one edit away only if + # t has one more character. + return ns + 1 == nt +``` + +```java +class Solution { + public boolean isOneEditDistance(String s, String t) { + int ns = s.length(); + int nt = t.length(); + + // Ensure that s is shorter than t. + if (ns > nt) return isOneEditDistance(t, s); + + // The strings are NOT one edit away distance + // if the length diff is more than 1. + if (nt - ns > 1) return false; + + for (int i = 0; i < ns; i++) { + if (s.charAt(i) != t.charAt(i)) { + if (ns == nt) { + // if strings have the same length + return s.substring(i + 1).equals(t.substring(i + 1)); + } else { + // If strings have different lengths + return s.substring(i).equals(t.substring(i + 1)); + } + } + } + + // If there are no diffs in ns distance + // The strings are one edit away only if + // t has one more character. + return (ns + 1 == nt); + } +} +``` + +```cpp +class Solution { +public: + bool isOneEditDistance(string s, string t) { + int ns = s.size(); + int nt = t.size(); + + // Ensure that s is shorter than t. + if (ns > nt) return isOneEditDistance(t, s); + + // The strings are NOT one edit away distance + // if the length diff is more than 1. + if (nt - ns > 1) return false; + + for (int i = 0; i < ns; i++) + if (s[i] != t[i]) + // if strings have the same length + if (ns == nt) return s.substr(i + 1) == t.substr(i + 1); + // If strings have different lengths + else + return s.substr(i) == t.substr(i + 1); + + // If there are no diffs in ns distance + // The strings are one edit away only if + // t has one more character. + return (ns + 1 == nt); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isOneEditDistance(s, t) { + let ns = s.length; + let nt = t.length; + + // Ensure that s is shorter than t. + if (ns > nt) return isOneEditDistance(t, s); + + // The strings are NOT one edit away distance + // if the length diff is more than 1. + if (nt - ns > 1) return false; + + for (let i = 0; i < ns; i++) + if (s[i] != t[i]) + if (ns == nt) + // if strings have the same length + return s.slice(i + 1) === t.slice(i + 1); + // If strings have different lengths + else return s.slice(i) === t.slice(i + 1); + + // If there are no diffs in ns distance + // The strings are one edit away only if + // t has one more character. + return ns + 1 === nt; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ in the worst case when string lengths are close enough `abs(ns - nt) <= 1`. $O(1)$ in the best case when `abs(ns - nt) > 1` +- Space complexity: $O(N)$ extra space used + +> where $N$ is the number of characters in the longest string From 79d84f1204e381677c49579dbacdf9e4673fe633 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 10 Nov 2025 02:52:10 -0800 Subject: [PATCH 11/32] fix one-edit-dsitance article --- articles/one-edit-distance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/one-edit-distance.md b/articles/one-edit-distance.md index 86bf0b11d..4a49582cf 100644 --- a/articles/one-edit-distance.md +++ b/articles/one-edit-distance.md @@ -106,7 +106,7 @@ class Solution { let nt = t.length; // Ensure that s is shorter than t. - if (ns > nt) return isOneEditDistance(t, s); + if (ns > nt) return this.isOneEditDistance(t, s); // The strings are NOT one edit away distance // if the length diff is more than 1. From 5c39389f185d916713c860d2857b524694f17da8 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 11 Nov 2025 00:14:05 -0800 Subject: [PATCH 12/32] add reverse-words-in-a-string-ii.md article --- articles/reverse-words-in-a-string-ii.md | 146 +++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 articles/reverse-words-in-a-string-ii.md diff --git a/articles/reverse-words-in-a-string-ii.md b/articles/reverse-words-in-a-string-ii.md new file mode 100644 index 000000000..d237f462d --- /dev/null +++ b/articles/reverse-words-in-a-string-ii.md @@ -0,0 +1,146 @@ +## 1. Reverse the Whole String and Then Reverse Each Word + +::tabs-start + +```python +class Solution: + def reverse(self, l: List[str], left: int, right: int) -> None: + while left < right: + l[left], l[right] = l[right], l[left] + left, right = left + 1, right - 1 + + def reverse_each_word(self, l: List[str]) -> None: + n = len(l) + start = end = 0 + + while start < n: + # go to the end of the word + while end < n and l[end] != ' ': + end += 1 + # reverse the word + self.reverse(l, start, end - 1) + # move to the next word + start = end + 1 + end += 1 + + def reverseWords(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + # reverse the whole string + self.reverse(s, 0, len(s) - 1) + + # reverse each word + self.reverse_each_word(s) +``` + +```java +class Solution { + public void reverse(char[] s, int left, int right) { + while (left < right) { + char tmp = s[left]; + s[left++] = s[right]; + s[right--] = tmp; + } + } + + public void reverseEachWord(char[] s) { + int n = s.length; + int start = 0, end = 0; + + while (start < n) { + // go to the end of the word + while (end < n && s[end] != ' ') ++end; + // reverse the word + reverse(s, start, end - 1); + // move to the next word + start = end + 1; + ++end; + } + } + + public void reverseWords(char[] s) { + // reverse the whole string + reverse(s, 0, s.length - 1); + + // reverse each word + reverseEachWord(s); + } +} +``` + +```cpp +class Solution { +public: + void reverseWords(vector& s) { + reverse(s.begin(), s.end()); + + // 'start' points to the beginning of the current word + // 'end' points to the position just after the current word + int start = 0, end = 0; + int n = s.size(); + + while (start < n) { + + // Move 'right' to the position just after the current word + while (end < n && s[end] != ' ') + end++; + + // Note: in C++, reverse() operates on [start, end) + // In other words, the leftmost element is included, while the rightmost element is not + reverse(s.begin() + start, s.begin() + end); + + // Move 'start' and 'end' to the beginning of the next word + end++; + start = end; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseWords(s) { + // reverse the whole string + this.reverse(s, 0, s.length - 1); + // reverse each word + this.reverseEachWord(s); + } + + reverse(s, left, right) { + while (left < right) { + let tmp = s[left]; + s[left++] = s[right]; + s[right--] = tmp; + } + } + + reverseEachWord(s) { + const n = s.length; + let start = 0, end = 0; + + while (start < n) { + // go to the end of the word + while (end < n && s[end] !== ' ') ++end; + // reverse the word + this.reverse(s, start, end - 1); + // move to the next word + start = end + 1; + ++end; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$, it's two passes along teh string. +- Space complexity: $O(1)$ constant space used + +> where $N$ is the length of the input `s` From fab7defad9ecba62b25ea7877305ad1116b48a32 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 11 Nov 2025 00:14:29 -0800 Subject: [PATCH 13/32] fix typo maximu-distance-in-arrays.md --- articles/maximum-distance-in-arrays.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/maximum-distance-in-arrays.md b/articles/maximum-distance-in-arrays.md index 2cea257e8..281479544 100644 --- a/articles/maximum-distance-in-arrays.md +++ b/articles/maximum-distance-in-arrays.md @@ -124,7 +124,7 @@ func maxDistance(arrays [][]int) int { ### Time & Space Complexity - Time complexity: $O((n * x)^2)$ -- Space complexity: $O(1)$ extra space used +- Space complexity: $O(1)$ constant space used > Where $n$ refers to the number of arrays in $arrays$ and $x$ refers to the average number of elements in each array in $arrays$. @@ -265,7 +265,7 @@ func max(a, b int) int { ### Time & Space Complexity - Time complexity: $O(n^2)$ -- Space complexity: $O(1)$ extra space used +- Space complexity: $O(1)$ constant space used > Where $n$ is the number of arrays in $arrays$ @@ -415,6 +415,6 @@ func min(a, b int) int { ### Time & Space Complexity - Time complexity: $O(n)$ -- Space complexity: $O(1)$ extra space used +- Space complexity: $O(1)$ constant space used > Where $n$ is the number of arrays in $arrays$ From 1a8c70da3813432ea9d44d02c3573822b0f2ae6a Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 11 Nov 2025 03:48:30 -0800 Subject: [PATCH 14/32] fix typo reverse-words-in-a-string-ii.md --- articles/reverse-words-in-a-string-ii.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/reverse-words-in-a-string-ii.md b/articles/reverse-words-in-a-string-ii.md index d237f462d..42387e3c9 100644 --- a/articles/reverse-words-in-a-string-ii.md +++ b/articles/reverse-words-in-a-string-ii.md @@ -140,7 +140,7 @@ class Solution { ### Time & Space Complexity -- Time complexity: $O(N)$, it's two passes along teh string. +- Time complexity: $O(N)$, it's two passes along the string. - Space complexity: $O(1)$ constant space used > where $N$ is the length of the input `s` From 5011e9ff30e40cb440e31955c0e32f967bfc5a1f Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 11 Nov 2025 03:50:23 -0800 Subject: [PATCH 15/32] add shortest-way-to-form-string.md article --- articles/shortest-way-to-form-string.md | 860 ++++++++++++++++++++++++ 1 file changed, 860 insertions(+) create mode 100644 articles/shortest-way-to-form-string.md diff --git a/articles/shortest-way-to-form-string.md b/articles/shortest-way-to-form-string.md new file mode 100644 index 000000000..e22a9228b --- /dev/null +++ b/articles/shortest-way-to-form-string.md @@ -0,0 +1,860 @@ +## 1. Concatenate until Subsequence + +::tabs-start + +```python +class Solution: + def shortestWay(self, source: str, target: str) -> int: + + # To check if to_check is subsequence of in_string + def is_subsequence(to_check, in_string): + i = j = 0 + while i < len(to_check) and j < len(in_string): + if to_check[i] == in_string[j]: + i += 1 + j += 1 + + return i == len(to_check) + + # Set of all characters of the source. We could use a boolean array as well. + source_chars = set(source) + + # Check if all characters of the target are present in the source + # If any character is not present, return -1 + for char in target: + if char not in source_chars: + return -1 + + # Concatenate source until the target is a subsequence + # of the concatenated string + concatenated_source = source + count = 1 + while not is_subsequence(target, concatenated_source): + concatenated_source += source + count += 1 + + # Number of concatenations done + return count +``` + +```java +class Solution { + public int shortestWay(String source, String target) { + + // Boolean array to mark all characters of source + boolean[] sourceChars = new boolean[26]; + for (char c : source.toCharArray()) { + sourceChars[c - 'a'] = true; + } + + // Check if all characters of the target are present in the source + // If any character is not present, return -1 + for (char c : target.toCharArray()) { + if (!sourceChars[c - 'a']) { + return -1; + } + } + + // Concatenate source until the target is a subsequence of the concatenated string + String concatenatedSource = source; + int count = 1; + while (!isSubsequence(target, concatenatedSource)) { + concatenatedSource += source; + count++; + } + + // Number of concatenations done + return count; + } + + // To check if toCheck is a subsequence of the inString + public boolean isSubsequence(String toCheck, String inString) { + int i = 0, j = 0; + while (i < toCheck.length() && j < inString.length()) { + if (toCheck.charAt(i) == inString.charAt(j)) { + i++; + } + j++; + } + + return i == toCheck.length(); + } +} +``` + +```cpp +class Solution { +public: + int shortestWay(string source, string target) { + + // Boolean array to mark all characters of the source + bool sourceChars[26] = {false}; + for (char c : source) { + sourceChars[c - 'a'] = true; + } + + // Check if all characters of the target are present in the source + // If any character is not present, return -1 + for (char c : target) { + if (!sourceChars[c - 'a']) { + return -1; + } + } + + // Concatenate source until the target is a subsequence of the concatenated string + string concatenatedSource = source; + int count = 1; + while (!isSubsequence(target, concatenatedSource)) { + concatenatedSource += source; + count++; + } + + // Number of concatenations done + return count; + } + + // To check if toCheck is a subsequence of inString + bool isSubsequence(string toCheck, string inString) { + int i = 0, j = 0; + while (i < toCheck.length() && j < inString.length()) { + if (toCheck[i] == inString[j]) { + i++; + } + j++; + } + + return i == toCheck.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} source + * @param {string} target + * @return {number} + */ + shortestWay(source, target) { + + // Boolean array to mark all characters of source + let sourceChars = new Array(26).fill(false); + for (let c of source) { + sourceChars[c.charCodeAt(0) - 'a'.charCodeAt(0)] = true; + } + + // Check if all characters of target are present in source + // If any character is not present, return -1 + for (let c of target) { + if (!sourceChars[c.charCodeAt(0) - 'a'.charCodeAt(0)]) { + return -1; + } + } + + // Concatenate source until target is a subsequence of concatenated string + let concatenatedSource = source; + let count = 1; + while (!this.isSubsequence(target, concatenatedSource)) { + concatenatedSource += source; + count++; + } + + // Number of concatenations done + return count; + } + + // To check if toCheck is a subsequence of inString + isSubsequence(toCheck, inString) { + let i = 0, j = 0; + while (i < toCheck.length && j < inString.length) { + if (toCheck[i] == inString[j]) { + i++; + } + j++; + } + + return i == toCheck.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(T^2 \cdot S)$ +- Space complexity: $O(TS)$ + +> where $S$ is the length of `source` and $T$ is the length of `target` + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def shortestWay(self, source: str, target: str) -> int: + + # Set of all characters of source. Can use a boolean array too. + source_chars = set(source) + + # Check if all characters of target are present in source + # If any character is not present, return -1 + for char in target: + if char not in source_chars: + return -1 + + # Length of source to loop back to start of source using mod + m = len(source) + + # Pointer for source + source_iterator = 0 + + # Number of times source is traversed. It will be incremented when + # while finding the occurrence of a character in target, source_iterator + # reaches the start of source again. + count = 0 + + # Find all characters of target in source + for char in target: + + # If while finding, iterator reaches start of source again, + # increment count + if source_iterator == 0: + count += 1 + + # Find the first occurrence of char in source + while source[source_iterator] != char: + + # Formula for incrementing while looping back to start. + source_iterator = (source_iterator + 1) % m + + # If while finding, iterator reaches the start of source again, + # increment count + if source_iterator == 0: + count += 1 + + # Loop will break when char is found in source. Thus, increment. + # Don't increment count until it is not clear that target has + # remaining characters. + source_iterator = (source_iterator + 1) % m + + # Return count + return count +``` + +```java +class Solution { + public int shortestWay(String source, String target) { + + // Boolean array to mark all characters of source + boolean[] sourceChars = new boolean[26]; + for (char c : source.toCharArray()) { + sourceChars[c - 'a'] = true; + } + + // Check if all characters of target are present in source + // If any character is not present, return -1 + for (char c : target.toCharArray()) { + if (!sourceChars[c - 'a']) { + return -1; + } + } + + // Length of source to loop back to start of source using mod + int m = source.length(); + + // Pointer for source + int sourceIterator = 0; + + // Number of times source is traversed. It will be incremented when + // While finding occurrence of a character in target, sourceIterator + // reaches the start of source again. + int count = 0; + + // Find all characters of target in source + for (char c : target.toCharArray()) { + + // If while finding, the iterator reaches the start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + + // Find the first occurrence of c in source + while (source.charAt(sourceIterator) != c) { + + // Formula for incrementing while looping back to start. + sourceIterator = (sourceIterator + 1) % m; + + // If while finding, iterator reaches start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + } + + // Loop will break when c is found in source. Thus, increment. + // Don't increment count until it is not clear that target has + // remaining characters. + sourceIterator = (sourceIterator + 1) % m; + } + + // Return count + return count; + } +} +``` + +```cpp +class Solution { +public: + int shortestWay(string source, string target) { + + // Boolean array to mark all characters of source + bool sourceChars[26] = {false}; + for (char c : source) { + sourceChars[c - 'a'] = true; + } + + // Check if all characters of target are present in source + // If any character is not present, return -1 + for (char c : target) { + if (!sourceChars[c - 'a']) { + return -1; + } + } + + // Length of source to loop back to start of source using mod + int m = source.length(); + + // Pointer for source + int sourceIterator = 0; + + // Number of times source is traversed. It will be incremented when + // while finding occurrence of a character in target, sourceIterator + // reaches the start of source again. + int count = 0; + + // Find all characters of target in source + for (char c : target) { + + // If while finding, iterator reaches start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + + // Find the first occurrence of c in source + while (source[sourceIterator] != c) { + + // Formula for incrementing while looping back to start. + sourceIterator = (sourceIterator + 1) % m; + + // If while finding, iterator reaches start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + } + + // Loop will break when c is found in source. Thus, increment. + // Don't increment count until it is not clear that target has + // remaining characters. + sourceIterator = (sourceIterator + 1) % m; + } + + // Return count + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} source + * @param {string} target + * @return {number} + */ + shortestWay(source, target) { + // Boolean array to mark all characters of source + let sourceChars = new Array(26).fill(false); + for (let c of source) { + sourceChars[c.charCodeAt(0) - 'a'.charCodeAt(0)] = true; + } + + // Check if all characters of target are present in source + // If any character is not present, return -1 + for (let c of target) { + if (!sourceChars[c.charCodeAt(0) - 'a'.charCodeAt(0)]) { + return -1; + } + } + + // Length of source to loop back to start of source using mod + let m = source.length; + + // Pointer for source + let sourceIterator = 0; + + // Number of times source is traversed. It will be incremented when + // while finding occurrence of a character in target, sourceIterator + // reaches the start of source again. + let count = 0; + + // Find all characters of target in source + for (let c of target) { + + // If while finding, iterator reaches start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + + // Find the first occurrence of c in source + while (source[sourceIterator] != c) { + + // Formula for incrementing while looping back to start. + sourceIterator = (sourceIterator + 1) % m; + + // If while finding, iterator reaches start of source again, + // increment count + if (sourceIterator == 0) { + count++; + } + } + + // Loop will break when c is found in source. Thus, increment. + // Don't increment count until it is not clear that target has + // remaining characters. + sourceIterator = (sourceIterator + 1) % m; + } + + // Return count + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(S \cdot T)$ +- Space complexity: $O(1)$ constant space used + +> where $S$ is the length of `source` and $T$ is the length of `target` + +--- + +## 3. Inverted Index and Binary Search + +::tabs-start + +```python + +``` + +```java +class Solution { + public int shortestWay(String source, String target) { + + // List of indices for all characters in source + ArrayList[] charToIndices = new ArrayList[26]; + for (int i = 0; i < source.length(); i++) { + char c = source.charAt(i); + if (charToIndices[c - 'a'] == null) { + charToIndices[c - 'a'] = new ArrayList<>(); + } + charToIndices[c - 'a'].add(i); + } + + // Pointer for source + int sourceIterator = 0; + + // Number of times we need to iterate through source + int count = 1; + + // Find all characters of target in source + for (char c : target.toCharArray()) { + + // If the character is not in the source, return -1 + if (charToIndices[c - 'a'] == null) { + return -1; + } + + // Binary search to find the index of the character in source + // next to the source iterator + ArrayList indices = charToIndices[c - 'a']; + int index = Collections.binarySearch(indices, sourceIterator); + + // If the index is negative, we need to find the next index + // that is greater than the source iterator + if (index < 0) { + index = -index - 1; + } + + // If we have reached the end of the list, we need to iterate + // through source again, hence first index of character in source. + if (index == indices.size()) { + count++; + sourceIterator = indices.get(0) + 1; + } else { + sourceIterator = indices.get(index) + 1; + } + } + + // Return the number of times we need to iterate through source + return count; + } +} +``` + +```cpp +class Solution { +public: + int shortestWay(string source, string target) { + + // Array to store the vector of charToIndices of each character in source + vector < int > charToIndices[26]; + for (int i = 0; i < source.size(); i++) { + charToIndices[source[i] - 'a'].push_back(i); + } + + // The current index in source + int sourceIterator = 0; + + // Number of times we have to iterate through source to get target + int count = 1; + + // Find all characters of target in source + for (int i = 0; i < target.size(); i++) { + + // If the character is not present in source, return -1 + if (charToIndices[target[i] - 'a'].size() == 0) { + return -1; + } + + // Binary search to find the index of the character in source next to the source iterator + vector < int > indices = charToIndices[target[i] - 'a']; + int index = lower_bound(indices.begin(), indices.end(), sourceIterator) - indices.begin(); + + // If we have reached the end of the list, we need to iterate + // through source again, hence first index of character in source. + if (index == indices.size()) { + count++; + sourceIterator = indices[0] + 1; + } else { + sourceIterator = indices[index] + 1; + } + } + + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} source + * @param {string} target + * @return {number} + */ + shortestWay(source, target) { + // List of indices for all characters in source + let charToIndices = new Array(26); + for (let i = 0; i < source.length; i++) { + let c = source[i]; + if (charToIndices[c.charCodeAt(0) - 'a'.charCodeAt(0)] == null) { + charToIndices[c.charCodeAt(0) - 'a'.charCodeAt(0)] = []; + } + charToIndices[c.charCodeAt(0) - 'a'.charCodeAt(0)].push(i); + } + // Pointer for source + let sourceIterator = 0; + // Number of times we need to iterate through source + let count = 1; + // Find all characters of target in source + for (let i = 0; i < target.length; i++) { + let c = target[i]; + // If the character is not in the source, return -1 + if (charToIndices[c.charCodeAt(0) - 'a'.charCodeAt(0)] == null) { + return -1; + } + // Binary search to find the index of the character in source + // next to the source iterator + let indices = charToIndices[c.charCodeAt(0) - 'a'.charCodeAt(0)]; + let index = this.binarySearch(indices, sourceIterator); + // If we have reached the end of the list, we need to iterate + // through source again, hence first index of character in source. + if (index == indices.length) { + count++; + sourceIterator = indices[0] + 1; + } else { + sourceIterator = indices[index] + 1; + } + } + // Return the number of times we need to iterate through source + return count; + } + + /** + * @param {number[]} arr + * @param {number} target + * @return {number} + */ + binarySearch(arr, target) { + let left = 0; + let right = arr.length - 1; + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (arr[mid] == target) { + return mid; + } else if (arr[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(S + T \log(S))$ +- Space complexity: $O(S)$ + +> where $S$ is the length of `source` and $T$ is the length of `target` + +--- + +## 4. 2D Array + +::tabs-start + +```python +class Solution: + def shortestWay(self, source: str, target: str) -> int: + + # Length of source + source_length = len(source) + + # Next Occurrence of Character after Index + next_occurrence = [defaultdict(int) for idx in range(source_length)] + + # Base Case + next_occurrence[source_length - + 1][source[source_length - 1]] = source_length - 1 + + # Using Recurrence Relation to fill next_occurrence + for idx in range(source_length - 2, -1, -1): + next_occurrence[idx] = next_occurrence[idx + 1].copy() + next_occurrence[idx][source[idx]] = idx + + # Pointer for source + source_iterator = 0 + + # Number of times we need to iterate through source + count = 1 + + # Find all characters of target in source + for char in target: + + # If character is not in source, return -1 + if char not in next_occurrence[0]: + return -1 + + # If we have reached the end of source, or the character is not in + # source after source_iterator, loop back to beginning + if (source_iterator == source_length or + char not in next_occurrence[source_iterator]): + count += 1 + source_iterator = 0 + + # Next occurrence of character in source after source_iterator + source_iterator = next_occurrence[source_iterator][char] + 1 + + # Return the number of times we need to iterate through source + return count +``` + +```java +class Solution { + public int shortestWay(String source, String target) { + + // Next occurrence of a character after a given index + int[][] nextOccurrence = new int[source.length()][26]; + + // Base Case + for (int c = 0; c < 26; c++) { + nextOccurrence[source.length() - 1][c] = -1; + } + nextOccurrence[source.length() - 1][source.charAt(source.length() - 1) - 'a'] = source.length() - 1; + + // Fill using recurrence relation + for (int idx = source.length() - 2; idx >= 0; idx--) { + for (int c = 0; c < 26; c++) { + nextOccurrence[idx][c] = nextOccurrence[idx + 1][c]; + } + nextOccurrence[idx][source.charAt(idx) - 'a'] = idx; + } + + // Pointer to the current index in source + int sourceIterator = 0; + + // Number of times we need to iterate through source + int count = 1; + + // Find all characters of target in source + for (char c : target.toCharArray()) { + + // If the character is not present in source + if (nextOccurrence[0][c - 'a'] == -1) { + return -1; + } + + // If we have reached the end of source, or the character is not in + // source after source_iterator, loop back to beginning + if (sourceIterator == source.length() || nextOccurrence[sourceIterator][c - 'a'] == -1) { + count++; + sourceIterator = 0; + } + + // Next occurrence of character in source after source_iterator + sourceIterator = nextOccurrence[sourceIterator][c - 'a'] + 1; + } + + // Return the number of times we need to iterate through source + return count; + } +} +``` + +```cpp +class Solution { +public: + int shortestWay(string source, string target) { + + // Next Occurrence of Character after Index + int nextOccurrence[source.length()][26]; + + // Base Case + for (int c = 0; c < 26; c++) { + nextOccurrence[source.length() - 1][c] = -1; + } + nextOccurrence[source.length() - 1][source[source.length() - 1] - 'a'] = source.length() - 1; + + // Fill using recurrence relation + for (int idx = source.length() - 2; idx >= 0; idx--) { + for (int c = 0; c < 26; c++) { + nextOccurrence[idx][c] = nextOccurrence[idx + 1][c]; + } + nextOccurrence[idx][source[idx] - 'a'] = idx; + } + + // Pointer to the current index in source + int sourceIterator = 0; + + // Number of times we need to iterate through source + int count = 1; + + // Find all characters of target in source + for (char c : target) { + + // If the character is not present in source + if (nextOccurrence[0][c - 'a'] == -1) { + return -1; + } + + // If we have reached the end of source, or the character is not in + // source after source_iterator, loop back to beginning + if (sourceIterator == source.length() || nextOccurrence[sourceIterator][c - 'a'] == -1) { + count++; + sourceIterator = 0; + } + + // Next occurrence of character in source after source_iterator + sourceIterator = nextOccurrence[sourceIterator][c - 'a'] + 1; + } + + // Return the number of times we need to iterate through source + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} source + * @param {string} target + * @return {number} + */ + shortestWay(source, target) { + // Length of source + const sourceLength = source.length + + // Next Occurrence of Character after Index + const nextOccurrence = Array.from({length: sourceLength}, () => Array(26).fill(-1)) + + // Base Case + for (let c = 0; c < 26; c++) { + nextOccurrence[sourceLength - 1][c] = -1 + } + nextOccurrence[sourceLength - 1][source[sourceLength - 1].charCodeAt(0) - 'a'.charCodeAt(0)] = sourceLength - 1 + + // Fill using the recurrence relation + for (let idx = sourceLength - 2; idx >= 0; idx--) { + for (let c = 0; c < 26; c++) { + nextOccurrence[idx][c] = nextOccurrence[idx + 1][c] + } + nextOccurrence[idx][source[idx].charCodeAt(0) - 'a'.charCodeAt(0)] = idx + } + + // Pointer to the current index in source + let sourceIterator = 0 + + // Number of times we need to iterate through source + let count = 1 + + // Find all characters of target in source + for (let idx = 0; idx < target.length; idx++) { + + // If the character is not present in source + if (nextOccurrence[0][target[idx].charCodeAt(0) - 'a'.charCodeAt(0)] == -1) { + return -1 + } + + // If we have reached the end of source, or the character is not in + // source after source_iterator, loop back to beginning + if (sourceIterator == sourceLength || nextOccurrence[sourceIterator][target[idx].charCodeAt(0) - 'a'.charCodeAt(0)] == -1) { + count++ + sourceIterator = 0 + } + + // Next occurrence of the character in source after source_iterator + sourceIterator = nextOccurrence[sourceIterator][target[idx].charCodeAt(0) - 'a'.charCodeAt(0)] + 1 + } + + // Return the number of times we need to iterate through source + return count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(S + T)$ +- Space complexity: $O(S)$ + +> where $S$ is the length of `source` and $T$ is the length of `target` From 007ae2ff046a760c4d27e4e306414eca92e716b9 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 11 Nov 2025 06:13:16 -0800 Subject: [PATCH 16/32] add longest-substring-with-at-most-k-distinct-characters.md article --- ...ring-with-at-most-k-distinct-characters.md | 453 ++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 articles/longest-substring-with-at-most-k-distinct-characters.md diff --git a/articles/longest-substring-with-at-most-k-distinct-characters.md b/articles/longest-substring-with-at-most-k-distinct-characters.md new file mode 100644 index 000000000..2f66bdc97 --- /dev/null +++ b/articles/longest-substring-with-at-most-k-distinct-characters.md @@ -0,0 +1,453 @@ +## 1. Binary Search + Fixed Size Sliding Window + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int: + n = len(s) + if k >= n: + return n + left, right = k, n + + def isValid(size): + counter = collections.Counter(s[:size]) + if len(counter) <= k: + return True + for i in range(size, n): + counter[s[i]] += 1 + counter[s[i - size]] -= 1 + if counter[s[i - size]] == 0: + del counter[s[i - size]] + if len(counter) <= k: + return True + return False + + while left < right: + mid = (left + right + 1) // 2 + + if isValid(mid): + left = mid + else: + right = mid - 1 + + return left +``` + +```java +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + int n = s.length(); + if (k >= n) { + return n; + } + + int left = k, right = n; + while (left < right) { + int mid = (left + right + 1) / 2; + + if (isValid(s, mid, k)) { + left = mid; + } else { + right = mid - 1; + } + } + + return left; + } + + private boolean isValid(String s, int size, int k) { + int n = s.length(); + Map counter = new HashMap<>(); + + for (int i = 0; i < size; i++) { + char c = s.charAt(i); + counter.put(c, counter.getOrDefault(c, 0) + 1); + } + + if (counter.size() <= k) { + return true; + } + + for (int i = size; i < n; i++) { + char c1 = s.charAt(i); + counter.put(c1, counter.getOrDefault(c1, 0) + 1); + char c2 = s.charAt(i - size); + counter.put(c2, counter.getOrDefault(c2, 0) - 1); + if (counter.get(c2) == 0) { + counter.remove(c2); + } + if (counter.size() <= k) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstringKDistinct(string s, int k) { + int n = s.length(); + if (k >= n) { + return n; + } + + int left = k, right = n; + while (left < right) { + int mid = (left + right + 1) / 2; + + if (isValid(s, mid, k)) { + left = mid; + } else { + right = mid - 1; + } + } + + return left; + } + +private: + bool isValid(string s, int size, int k) { + int n = s.length(); + unordered_map counter; + + for (int i = 0; i < size; i++) { + char c = s[i]; + counter[c]++; + } + + if (counter.size() <= k) { + return true; + } + + for (int i = size; i < n; i++) { + char c1 = s[i]; + counter[c1]++; + char c2 = s[i - size]; + counter[c2]--; + if (counter[c2] == 0) { + counter.erase(c2); + } + if (counter.size() <= k) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + lengthOfLongestSubstringKDistinct(s, k) { + let n = s.length; + if (k >= n) { + return n; + } + + let left = k, right = n; + while (left < right) { + let mid = Math.floor((left + right + 1) / 2); + + if (this.isValid(s, mid, k)) { + left = mid; + } else { + right = mid - 1; + } + } + + return left; + } + + /** + * @param {string} s + * @param {number} size + * @param {number} k + * @return {boolean} + */ + isValid(s, size, k) { + let n = s.length; + let counter = new Map(); + + for (let i = 0; i < size; i++) { + let c = s.charAt(i); + counter.set(c, (counter.get(c) || 0) + 1); + } + + if (counter.size <= k) { + return true; + } + + for (let i = size; i < n; i++) { + let c1 = s.charAt(i); + counter.set(c1, (counter.get(c1) || 0) + 1); + let c2 = s.charAt(i - size); + counter.set(c2, (counter.get(c2) || 0) - 1); + if (counter.get(c2) === 0) { + counter.delete(c2); + } + if (counter.size <= k) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \log n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the length of the input string `s` and $k$ is the maximum number of distinct characters. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int: + n = len(s) + max_size = 0 + counter = collections.Counter() + + left = 0 + for right in range(n): + counter[s[right]] += 1 + + while len(counter) > k: + counter[s[left]] -= 1 + if counter[s[left]] == 0: + del counter[s[left]] + left += 1 + + max_size = max(max_size, right - left + 1) + + return max_size +``` + +```java +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + int n = s.length(); + int maxSize = 0; + Map counter = new HashMap<>(); + + int left = 0; + for (int right = 0; right < n; right++) { + counter.put(s.charAt(right), counter.getOrDefault(s.charAt(right), 0) + 1); + + while (counter.size() > k) { + counter.put(s.charAt(left), counter.get(s.charAt(left)) - 1); + if (counter.get(s.charAt(left)) == 0) { + counter.remove(s.charAt(left)); + } + left++; + } + + maxSize = Math.max(maxSize, right - left + 1); + } + + return maxSize; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstringKDistinct(string s, int k) { + int n = s.length(); + int maxSize = 0; + unordered_map counter; + + int left = 0; + for (int right = 0; right < n; right++) { + counter[s[right]]++; + + while (counter.size() > k) { + counter[s[left]]--; + if (counter[s[left]] == 0) { + counter.erase(s[left]); + } + left++; + } + + maxSize = max(maxSize, right - left + 1); + } + + return maxSize; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + lengthOfLongestSubstringKDistinct(s, k) { + let n = s.length; + let maxSize = 0; + let counter = new Map(); + + let left = 0; + for (let right = 0; right < n; right++) { + counter.set(s.charAt(right), (counter.get(s.charAt(right)) || 0) + 1); + + while (counter.size > k) { + counter.set(s.charAt(left), counter.get(s.charAt(left)) - 1); + if (counter.get(s.charAt(left)) === 0) { + counter.delete(s.charAt(left)); + } + left++; + } + + maxSize = Math.max(maxSize, right - left + 1); + } + + return maxSize; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(k)$ + +> Where $n$ is the length of the input string `s` and $k$ is the maximum number of distinct characters. + +--- + +## 3. Sliding Window II + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int: + max_size = 0 + counter = collections.Counter() + + for right in range(len(s)): + counter[s[right]] += 1 + + if len(counter) <= k: + max_size += 1 + else: + counter[s[right - max_size]] -= 1 + if counter[s[right - max_size]] == 0: + del counter[s[right - max_size]] + + return max_size +``` + +```java +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + int n = s.length(); + int maxSize = 0; + Map counter = new HashMap<>(); + + for (int right = 0; right < n; right++) { + counter.put(s.charAt(right), counter.getOrDefault(s.charAt(right), 0) + 1); + + if (counter.size() <= k) { + maxSize++; + } else { + counter.put(s.charAt(right - maxSize), counter.get(s.charAt(right - maxSize)) - 1); + if (counter.get(s.charAt(right - maxSize)) == 0) { + counter.remove(s.charAt(right - maxSize)); + } + } + } + + return maxSize; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstringKDistinct(string s, int k) { + int n = s.length(); + int maxSize = 0; + unordered_map counter; + + for (int right = 0; right < n; right++) { + counter[s[right]]++; + + if (counter.size() <= k) { + maxSize++; + } else { + counter[s[right - maxSize]]--; + if (counter[s[right - maxSize]] == 0) { + counter.erase(s[right - maxSize]); + } + } + } + return maxSize; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + lengthOfLongestSubstringKDistinct(s, k) { + let n = s.length; + let maxSize = 0; + let counter = new Map(); + + for (let right = 0; right < n; right++) { + counter.set(s.charAt(right), (counter.get(s.charAt(right)) || 0) + 1); + + if (counter.size <= k) { + maxSize++; + } else { + counter.set(s.charAt(right - maxSize), counter.get(s.charAt(right - maxSize)) - 1); + if (counter.get(s.charAt(right - maxSize)) === 0) { + counter.delete(s.charAt(right - maxSize)); + } + } + } + return maxSize; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the length of the input string `s` and $k$ is the maximum number of distinct characters. From 5b17b2c0da53b9752289d5e096d87c2d05d88b4e Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 12 Nov 2025 00:31:50 -0800 Subject: [PATCH 17/32] add max-consecutive-ones-ii.md article --- articles/max-consecutive-ones-ii.md | 264 ++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 articles/max-consecutive-ones-ii.md diff --git a/articles/max-consecutive-ones-ii.md b/articles/max-consecutive-ones-ii.md new file mode 100644 index 000000000..88d0cb414 --- /dev/null +++ b/articles/max-consecutive-ones-ii.md @@ -0,0 +1,264 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMaxConsecutiveOnes(self, nums: List[int]) -> int: + longest_sequence = 0 + + for left in range(len(nums)): + num_zeroes = 0 + for right in range(left, len(nums)): # Check every consecutive sequence + if num_zeroes == 2: + break + if nums[right] == 0: # Count how many 0's + num_zeroes += 1 + if num_zeroes <= 1: # Update answer if it's valid + longest_sequence = max(longest_sequence, right - left + 1) + + return longest_sequence +``` + +```java +class Solution { + public int findMaxConsecutiveOnes(int[] nums) { + int longestSequence = 0; + for (int left = 0; left < nums.length; left++) { + int numZeroes = 0; + + //Check every consecutive sequence + for (int right = left; right < nums.length; right++) { + // Count how many 0's + if (nums[right] == 0) { + numZeroes += 1; + } + // Update answer if it's valid + if (numZeroes <= 1) { + longestSequence = Math.max(longestSequence, right - left + 1); + } + } + } + return longestSequence; + } +} +``` + +```cpp +class Solution { +public: + int findMaxConsecutiveOnes(vector& nums) { + int longestSequence = 0; + + for (int left = 0; left < nums.size(); left++) { + int numZeroes = 0; + + //Check every consecutive sequence + for (int right = left; right < nums.size(); right++) { + + // Count how many 0's + if (nums[right] == 0) { + numZeroes += 1; + } + + // Update answer if it's valid + if (numZeroes <= 1) { + longestSequence = max(longestSequence, right - left + 1); + } + } + } + + return longestSequence; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxConsecutiveOnes(nums) { + let longestSequence = 0; + + for (let left = 0; left < nums.length; left++) { + let numZeroes = 0; + + //Check every consecutive sequence + for (let right = left; right < nums.length; right++) { + + // Count how many 0's + if (nums[right] === 0) { + numZeroes += 1; + } + + // Update answer if it's valid + if (numZeroes <= 1) { + longestSequence = Math.max(longestSequence, right - left + 1); + } + } + } + + return longestSequence; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n^2)$ +- Space complexity: $O(1)$ constant space used + +> Where $n$ is the length of the input array `nums`. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def findMaxConsecutiveOnes(self, nums: List[int]) -> int: + longest_sequence = 0 + left, right = 0, 0 + num_zeroes = 0 + + while right < len(nums): # While our window is in bounds + if nums[right] == 0: # Increase num_zeroes if the rightmost element is 0 + num_zeroes += 1 + + while num_zeroes == 2: # If our window is invalid, contract our window + if nums[left] == 0: + num_zeroes -= 1 + left += 1 + + longest_sequence = max(longest_sequence, right - left + 1) # Update our longest sequence answer + right += 1 # Expand our window + + return longest_sequence +``` + +```java +class Solution { + public int findMaxConsecutiveOnes(int[] nums) { + int longestSequence = 0; + int left = 0; + int right = 0; + int numZeroes = 0; + + // While our window is in bounds + while (right < nums.length) { + + // Increase numZeroes if the rightmost element is 0 + if (nums[right] == 0) { + numZeroes++; + } + + //If our window is invalid, contract our window + while (numZeroes == 2) { + if (nums[left] == 0) { + numZeroes--; + } + left++; + } + + // Update our longest sequence answer + longestSequence = Math.max(longestSequence, right - left + 1); + + // Expand our window + right++; + } + return longestSequence; + } +} +``` + +```cpp +class Solution { +public: + int findMaxConsecutiveOnes(vector& nums) { + int longestSequence = 0; + int left = 0; + int right = 0; + int numZeroes = 0; + + // While our window is in bounds + while (right < nums.size()) { + + // Increase numZeroes if the rightmost element is 0 + if (nums[right] == 0) { + numZeroes++; + } + + //If our window is invalid, contract our window + while (numZeroes == 2) { + if (nums[left] == 0) { + numZeroes--; + } + left++; + } + + // Update our longest sequence answer + longestSequence = max(longestSequence, right - left + 1); + + // Expand our window + right++; + } + + return longestSequence; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxConsecutiveOnes(nums) { + let longestSequence = 0; + let left = 0; + let right = 0; + let numZeroes = 0; + + // While our window is in bounds + while (right < nums.length) { + + // Increase numZeroes if the rightmost element is 0 + if (nums[right] === 0) { + numZeroes++; + } + + //If our window is invalid, contract our window + while (numZeroes === 2) { + if (nums[left] === 0) { + numZeroes--; + } + left++; + } + + // Update our longest sequence answer + longestSequence = Math.max(longestSequence, right - left + 1); + + // Expand our window + right++; + } + + return longestSequence; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ constant space used + +> Where $n$ is the length of the input array `nums`. From 7739a32ac83275cf462d5a0620e0b059c73d6a7a Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 12 Nov 2025 03:18:24 -0800 Subject: [PATCH 18/32] add find-k-length-substrings-with-no-repeated-characters.md article --- ...-substrings-with-no-repeated-characters.md | 370 ++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 articles/find-k-length-substrings-with-no-repeated-characters.md diff --git a/articles/find-k-length-substrings-with-no-repeated-characters.md b/articles/find-k-length-substrings-with-no-repeated-characters.md new file mode 100644 index 000000000..d8a17fbe0 --- /dev/null +++ b/articles/find-k-length-substrings-with-no-repeated-characters.md @@ -0,0 +1,370 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numKLenSubstrNoRepeats(self, s: str, k: int) -> int: + if k > 26: + return 0 + answer = 0 + n = len(s) + + for i in range(n - k + 1): + # Initializing an empty frequency array + freq = [0] * 26 + + for j in range(i, i + k): + curr_char = ord(s[j]) - ord("a") + + # Incrementing the frequency of current character + freq[curr_char] += 1 + + # If a repeated character is found, we stop the loop + if freq[curr_char] > 1: + break + else: + # If the substring does not have any repeated characters, + # we increment the answer + answer += 1 + + return answer +``` + +```java +class Solution { + public int numKLenSubstrNoRepeats(String s, int k) { + if (k > 26) return 0; + int n = s.length(); + int answer = 0; + + for (int i = 0; i <= n - k; i++) { + // Initializing an empty frequency array + int freq[] = new int[26]; + boolean isUnique = true; + + for (int j = i; j < i + k; j++) { + char ch = s.charAt(j); + + // Incrementing the frequency of current character + freq[ch - 'a']++; + + // If a repeated character is found, we stop the loop + if (freq[ch - 'a'] > 1) { + isUnique = false; + break; + } + } + + // If the substring does not have any repeated characters, + // we increment the answer + if (isUnique) { + answer++; + } + } + + return answer; + } +} +``` + +```cpp +class Solution { +public: + int numKLenSubstrNoRepeats(string s, int k) { + if (k > 26) return 0; + + int answer = 0; + int n = s.size(); + + for (int i = 0; i <= n - k; i++) { + // Initializing an empty frequency array + int freq[26] = {0}; + bool isUnique = true; + + for (int j = i; j < i + k; j++) { + // Incrementing the frequency of current character + freq[s[j] - 'a']++; + + // If a repeated character is found, we stop the loop + if (freq[s[j] - 'a'] > 1) { + isUnique = false; + break; + } + } + + // If the substring does not have any repeated characters, + // we increment the answer + if (isUnique) { + answer++; + } + } + + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + numKLenSubstrNoRepeats(s, k) { + if (k > 26) { + return 0; + } + + let answer = 0; + const n = s.length; + + for (let i = 0; i < n - k + 1; i++) { + // Initializing an empty frequency array + const freq = new Array(26).fill(0); + let hasRepeats = false; + + for (let j = i; j < i + k; j++) { + const currChar = s[j].charCodeAt(0) - 'a'.charCodeAt(0); + // Incrementing the frequency of current character + freq[currChar]++; + // If a repeated character is found, we stop the loop + if (freq[currChar] > 1) { + hasRepeats = true; + break; + } + } + + // If the substring does not have any repeated characters, + // we increment the answer + if (!hasRepeats) { + answer++; + } + } + + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \min(m, k))$ +- Space complexity: $O(m)$ + +> Where $n$ is the length of `s`, $k$ is the given substring length, and $m$ is the number of unique characters allowed in the string. In this case, $m=26$. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def numKLenSubstrNoRepeats(self, s: str, k: int) -> int: + # We can reuse the condition from the first approach + # as for k > 26, there can be no substrings with only unique characters + if k > 26: + return 0 + answer = 0 + n = len(s) + + # Initializing the left and right pointers + left = right = 0 + + # Initializing an empty frequency array + freq = [0] * 26 + + # Function to obtain the index of a character according to the alphabet + def get_val(ch: str) -> int: + return ord(ch) - ord("a") + + while right < n: + + # Add the current character in the frequency array + freq[get_val(s[right])] += 1 + + # If the current character appears more than once in the frequency array + # keep contracting the window and removing characters from the + # frequency array till the frequency of the current character becomes 1. + while freq[get_val(s[right])] > 1: + freq[get_val(s[left])] -= 1 + left += 1 + + # Check if the length of the current unique substring is equal to k + if right - left + 1 == k: + answer += 1 + + # Contract the window and remove the leftmost character from the + # frequency array + freq[get_val(s[left])] -= 1 + left += 1 + + # Expand the window + right += 1 + + return answer +``` + +```java +class Solution { + public int numKLenSubstrNoRepeats(String s, int k) { + // We can reuse the condition from the first approach + // as for k > 26, there can be no substrings with only unique characters + if (k > 26) return 0; + + int answer = 0; + int n = s.length(); + + // Initializing the left and right pointers + int left = 0, right = 0; + // Initializing an empty frequency array + int freq[] = new int[26]; + + while (right < n) { + // Add the current character in the frequency array + freq[s.charAt(right) - 'a']++; + + // If the current character appears more than once in the frequency array + // keep contracting the window and removing characters from the + // frequency array till the frequency of the current character becomes 1. + while (freq[s.charAt(right) - 'a'] > 1) { + freq[s.charAt(left) - 'a']--; + left++; + } + + // Check if the length of the current unique substring is equal to k + if (right - left + 1 == k) { + answer++; + + // Contract the window and remove the leftmost character from the + // frequency array + freq[s.charAt(left) - 'a']--; + left++; + } + + // Expand the window + right++; + } + + return answer; + } +} +``` + +```cpp +class Solution { +public: + int numKLenSubstrNoRepeats(string s, int k) { + // We can reuse the condition from the first approach + // as for k > 26, there can be no substrings with only unique characters + if (k > 26) return 0; + + int answer = 0; + int n = s.size(); + + // Initializing the left and right pointers + int left = 0, right = 0; + // Initializing an empty frequency array + int freq[26] = {0}; + + while (right < n) { + // Add the current character in the frequency array + freq[s[right] - 'a']++; + + // If the current character appears more than once in the frequency + // array keep contracting the window and removing characters from + // the frequency array till the frequency of the current character + // becomes 1. + while (freq[s[right] - 'a'] > 1) { + freq[s[left] - 'a']--; + left++; + } + + // Check if the length of the current unique substring is equal to k + if (right - left + 1 == k) { + answer++; + + // Contract the window and remove the leftmost character from + // the frequency array + freq[s[left] - 'a']--; + left++; + } + + // Expand the window + right++; + } + + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + numKLenSubstrNoRepeats(s, k) { + // We can reuse the condition from the first approach + // as for k > 26, there can be no substrings with only unique characters + if (k > 26) { + return 0; + } + + let answer = 0; + const n = s.length; + // Initializing the left and right pointers + let left = 0, right = 0; + // Initializing an empty frequency array + const freq = new Array(26).fill(0); + + // Function to obtain the index of a character according to the alphabet + const getVal = (ch) => { + return ch.charCodeAt(0) - 'a'.charCodeAt(0); + }; + + while (right < n) { + // Add the current character in the frequency array + freq[getVal(s[right])]++; + + // If the current character appears more than once in the frequency array + // keep contracting the window and removing characters from the + // frequency array till the frequency of the current character becomes 1. + while (freq[getVal(s[right])] > 1) { + freq[getVal(s[left])]--; + left++; + } + + // Check if the length of the current unique substring is equal to k + if (right - left + 1 === k) { + answer++; + // Contract the window and remove the leftmost character from the + // frequency array + freq[getVal(s[left])]--; + left++; + } + + // Expand the window + right++; + } + + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(m)$ + +> Where $n$ is the length of `s` and $m$ is the number of unique characters allowed in the string. In this case, $m=26$. From 92697e8ee90ee2c55665b8614110961d09634252 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 13 Nov 2025 05:36:24 -0800 Subject: [PATCH 19/32] add find-anagram-mappings.md article --- articles/find-anagram-mappings.md | 322 ++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 articles/find-anagram-mappings.md diff --git a/articles/find-anagram-mappings.md b/articles/find-anagram-mappings.md new file mode 100644 index 000000000..aa15d7101 --- /dev/null +++ b/articles/find-anagram-mappings.md @@ -0,0 +1,322 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def anagramMappings(self, nums1: List[int], nums2: List[int]) -> List[int]: + # List to store the anagram mappings. + mappings = [0] * len(nums1) + + for i in range(len(nums1)): + for j in range(len(nums2)): + # Store the corresponding index of number in the second list. + if nums1[i] == nums2[j]: + mappings[i] = j + break + + return mappings +``` + +```java +class Solution { + public int[] anagramMappings(int[] nums1, int[] nums2) { + // List to store the anagram mappings. + int[] mappings = new int[nums1.length]; + + for (int i = 0; i < nums1.length; i++) { + for (int j = 0; j < nums2.length; j++) { + // Store the corresponding index of number in the second list. + if (nums1[i] == nums2[j]) { + mappings[i] = j; + break; + } + } + } + return mappings; + } +} +``` + +```cpp +class Solution { +public: + vector anagramMappings(vector& nums1, vector& nums2) { + // List to store the anagram mappings. + vector mappings; + + for (int num : nums1) { + for (int i = 0; i < nums2.size(); i++) { + // Store the corresponding index of number in the second list. + if (num == nums2[i]) { + mappings.push_back(i); + break; + } + } + } + return mappings; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + anagramMappings(nums1, nums2) { + // Array to store the anagram mappings. + const mappings = new Array(nums1.length); + + for (let i = 0; i < nums1.length; i++) { + for (let j = 0; j < nums2.length; j++) { + // Store the corresponding index of number in the second array. + if (nums1[i] === nums2[j]) { + mappings[i] = j; + break; + } + } + } + return mappings; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N^2)$ +- Space complexity: $O(1)$ constant space + +> Where N is the number of integers in the list `nums1` and `nums2`. + +--- + +## 2. HashMap + +::tabs-start + +```python +class Solution: + def anagramMappings(self, nums1: List[int], nums2: List[int]) -> List[int]: + # Store the index corresponding to the value in the second list. + valueToPos = {} + for i in range(len(nums2)): + valueToPos[nums2[i]] = i + + # List to store the anagram mappings. + mappings = [0] * len(nums1) + for i in range(len(nums1)): + mappings[i] = valueToPos[nums1[i]] + + return mappings +``` + +```java +class Solution { + public int[] anagramMappings(int[] nums1, int[] nums2) { + // Store the index corresponding to the value in the second list. + HashMap valueToPos = new HashMap<>(); + for (int i = 0; i < nums2.length; i++) { + valueToPos.put(nums2[i], i); + } + + // List to store the anagram mappings. + int[] mappings = new int[nums1.length]; + for (int i = 0; i < nums1.length; i++) { + mappings[i] = valueToPos.get(nums1[i]); + } + + return mappings; + } +} +``` + +```cpp +class Solution { +public: + vector anagramMappings(vector& nums1, vector& nums2) { + // Store the index corresponding to the value in the second list. + unordered_map valueToPos; + for (int i = 0; i < nums2.size(); i++) { + valueToPos[nums2[i]] = i; + } + + // List to store the anagram mappings. + vector mappings; + for (int num : nums1) { + mappings.push_back(valueToPos[num]); + } + + return mappings; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + anagramMappings(nums1, nums2) { + // Store the index corresponding to the value in the second list. + const valueToPos = new Map(); + for (let i = 0; i < nums2.length; i++) { + valueToPos.set(nums2[i], i); + } + + // List to store the anagram mappings. + const mappings = new Array(nums1.length); + for (let i = 0; i < nums1.length; i++) { + mappings[i] = valueToPos.get(nums1[i]); + } + + return mappings; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where N is the number of integers in the list `nums1` and `nums2`. + +--- + +## 3. Bit Manipulation + Sorting + +::tabs-start + +```python +class Solution: + def anagramMappings(self, nums1: List[int], nums2: List[int]) -> List[int]: + bitsToShift = 7 + numToGetLastBits = (1 << bitsToShift) - 1 + + # Store the index within the integer itself. + for i in range(len(nums1)): + nums1[i] = (nums1[i] << bitsToShift) + i + nums2[i] = (nums2[i] << bitsToShift) + i + + # Sort both lists so that the original integers end up at the same index. + nums1.sort() + nums2.sort() + + # List to store the anagram mappings. + mappings = [0] * len(nums1) + + for i in range(len(nums1)): + # Store the index in the second list corresponding to the integer index in the first list. + mappings[nums1[i] & numToGetLastBits] = (nums2[i] & numToGetLastBits) + + return mappings +``` + +```java +class Solution { + final int bitsToShift = 7; + final int numToGetLastBits = (1 << bitsToShift) - 1; + + public int[] anagramMappings(int[] nums1, int[] nums2) { + // Store the index within the integer itself. + for (int i = 0; i < nums1.length; i++) { + nums1[i] = (nums1[i] << bitsToShift) + i; + nums2[i] = (nums2[i] << bitsToShift) + i; + } + + // Sort both lists so that the original integers end up at the same index. + Arrays.sort(nums1); + Arrays.sort(nums2); + + // List to store the anagram mappings. + int[] mappings = new int[nums1.length]; + for (int i = 0; i < nums1.length; i++) { + // Store the index in the second list corresponding to the integer index in the first list. + mappings[nums1[i] & numToGetLastBits] = (nums2[i] & numToGetLastBits); + } + + return mappings; + } +} +``` + +```cpp +class Solution { +public: + const int bitsToShift = 7; + const int numToGetLastBits = (1 << bitsToShift) - 1; + + vector anagramMappings(vector& nums1, vector& nums2) { + // Store the index within the integer itself. + for (int i = 0; i < nums1.size(); i++) { + nums1[i] = (nums1[i] << bitsToShift) + i; + nums2[i] = (nums2[i] << bitsToShift) + i; + } + + // Sort both lists so that the original integers end up at the same index. + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + // List to store the anagram mappings. + vector mappings(nums1.size()); + for (int i = 0; i < nums1.size(); i++) { + // Store the index in the second list corresponding to the integer index in the first list. + mappings[nums1[i] & numToGetLastBits] = (nums2[i] & numToGetLastBits); + } + + return mappings; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + anagramMappings(nums1, nums2) { + const bitsToShift = 7; + const numToGetLastBits = (1 << bitsToShift) - 1; + + // Store the index within the integer itself. + for (let i = 0; i < nums1.length; i++) { + nums1[i] = (nums1[i] << bitsToShift) + i; + nums2[i] = (nums2[i] << bitsToShift) + i; + } + + // Sort both arrays so that the original integers end up at the same index. + nums1.sort((a, b) => a - b); + nums2.sort((a, b) => a - b); + + // Array to store the anagram mappings. + const mappings = new Array(nums1.length); + + for (let i = 0; i < nums1.length; i++) { + // Store the index in the second array corresponding to the integer index in the first array. + mappings[nums1[i] & numToGetLastBits] = (nums2[i] & numToGetLastBits); + } + + return mappings; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N \log N)$ +- Space complexity: $O(\log N)$ + +> Where N is the number of integers in the list `nums1` and `nums2`. From 22b5ddfc7d3ce96fb60d80ab16575963cf955fd3 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 13 Nov 2025 08:06:51 -0800 Subject: [PATCH 20/32] fix latex styling find-anagram-mappings.md --- articles/find-anagram-mappings.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/find-anagram-mappings.md b/articles/find-anagram-mappings.md index aa15d7101..16bea8cf2 100644 --- a/articles/find-anagram-mappings.md +++ b/articles/find-anagram-mappings.md @@ -91,7 +91,7 @@ class Solution { - Time complexity: $O(N^2)$ - Space complexity: $O(1)$ constant space -> Where N is the number of integers in the list `nums1` and `nums2`. +> Where $N$ is the number of integers in the list `nums1` and `nums2`. --- @@ -188,7 +188,7 @@ class Solution { - Time complexity: $O(N)$ - Space complexity: $O(N)$ -> Where N is the number of integers in the list `nums1` and `nums2`. +> Where $N$ is the number of integers in the list `nums1` and `nums2`. --- @@ -319,4 +319,4 @@ class Solution { - Time complexity: $O(N \log N)$ - Space complexity: $O(\log N)$ -> Where N is the number of integers in the list `nums1` and `nums2`. +> Where $N$ is the number of integers in the list `nums1` and `nums2`. From 80b1a9677c304f4be8170cfb274a0cbd14541f9e Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 13 Nov 2025 08:07:15 -0800 Subject: [PATCH 21/32] add palindrome-permutation.md article --- articles/palindrome-permutation.md | 403 +++++++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 articles/palindrome-permutation.md diff --git a/articles/palindrome-permutation.md b/articles/palindrome-permutation.md new file mode 100644 index 000000000..9a4d47831 --- /dev/null +++ b/articles/palindrome-permutation.md @@ -0,0 +1,403 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def canPermutePalindrome(self, s: str) -> bool: + count = 0 + for i in range(128): # For all ASCII characters + if count > 1: + break + ct = 0 + for j in range(len(s)): + if s[j] == chr(i): # Comparing with ASCII character + ct += 1 + count += ct % 2 + return count <= 1 +``` + +```java +class Solution { + public boolean canPermutePalindrome(String s) { + int count = 0; + for (char i = 0; i < 128 && count <= 1; i++) { + int ct = 0; + for (int j = 0; j < s.length(); j++) { + if (s.charAt(j) == i) ct++; + } + count += ct % 2; + } + return count <= 1; + } +} +``` + +```cpp +class Solution { +public: + bool canPermutePalindrome(string s) { + int count[128] = {0}; + for (int j = 0; j < s.length(); j++) { + count[s[j]]++; + } + int odd = 0; + for (int i = 0; i < 128 && odd <= 1; i++) { + odd += count[i] % 2; + } + return odd <= 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + canPermutePalindrome(s) { + let count = 0; + for (let i = 0; i < 128 && count <= 1; i++) { + let ct = 0; + for (let j = 0; j < s.length; j++) { + if (s.charCodeAt(j) === i) ct++; + } + count += ct % 2; + } + return count <= 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ + - If we generalize the solution to handle any Unicode character (no hardcoding): $O(k \cdot n)$.
$O(n^2)$ In the worst case, if all characters are unique $(k = n)$ +- Space complexity: $O(1)$ If the we assume the input contains only ASCII characters. + - $O(1)$ If the implementation is modiifed to handle Unicode characters. + +> Where $n$ is the size of the input string `s` and where $k$ is the number of unique characters in `s` + +--- + +## 2. Using HashMap + +::tabs-start + +```python +class Solution: + def canPermutePalindrome(self, s: str) -> bool: + from collections import Counter + + count = Counter(s) + odds = sum(val % 2 for val in count.values()) + return odds <= 1 +``` + +```java +class Solution { + public boolean canPermutePalindrome(String s) { + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1); + } + int count = 0; + for (char key : map.keySet()) { + count += map.get(key) % 2; + } + return count <= 1; + } +} +``` + +```cpp +class Solution { +public: + bool canPermutePalindrome(string s) { + unordered_map map; + for (int i = 0; i < s.length(); i++) { + +map[s[i]]++; + } + int count = 0; + for (auto& pair : map) { + count += pair.second % 2; + } + return count <= 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + canPermutePalindrome(s) { + const map = new Map(); + for (let i = 0; i < s.length; i++) { + map.set(s.charAt(i), (map.get(s.charAt(i)) || 0) + 1); + } + let count = 0; + for (let key of map.keys()) { + count += map.get(key) % 2; + } + return count <= 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ + - $O(n + k)$ If we generalize the solution to handle any Unicode character. In the worst case $(k = n)$, this becomes $O(n)$. +- Space complexity: $O(1)$ If the we assume the input contains only ASCII characters. + - $O(k)$ If the implementation is modiifed to handle Unicode characters, the space complexity would depend on the number of unique characters in the string. $O(n)$ in the worst case (if all characters are unique). + +> Where $n$ is the size of the input string `s` and where $k$ is the number of unique characters in `s` + +--- + +## 3. Using Array + +::tabs-start + +```python +class Solution: + def canPermutePalindrome(self, s: str) -> bool: + map = [0] * 128 + for ch in s: + map[ord(ch)] += 1 + count = 0 + for c in map: + if c % 2: + count += 1 + return count <= 1 +``` + +```java +class Solution { + public boolean canPermutePalindrome(String s) { + int[] map = new int[128]; + for (int i = 0; i < s.length(); i++) { + map[s.charAt(i)]++; + } + int count = 0; + for (int key = 0; key < map.length && count <= 1; key++) { + count += map[key] % 2; + } + return count <= 1; + } +} +``` + +```cpp +class Solution { +public: + bool canPermutePalindrome(string s) { + int map[128] = {0}; + for (char c : s) { + map[int(c)]++; + } + int count = 0; + for (int i = 0; i < 128 && count <= 1; i++) { + count += map[i] % 2; + } + return count <= 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + canPermutePalindrome(s) { + const map = new Array(128).fill(0); + for (let i = 0; i < s.length; i++) { + map[s.charCodeAt(i)]++; + } + let count = 0; + for (let key = 0; key < map.length && count <= 1; key++) { + count += map[key] % 2; + } + return count <= 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ If the we assume the input contains only ASCII characters. + - $O(k)$ If the implementation is modiifed to handle Unicode characters, the space complexity would depend on the number of unique characters in the string. $O(n)$ in the worst case (if all characters are unique). + +> Where $n$ is the size of the input string `s` and where $k$ is the number of unique characters in `s` + +--- + +## 4. Single Pass + +::tabs-start + +```python +class Solution: + def canPermutePalindrome(self, s: str) -> bool: + map = [0] * 128 + count = 0 + for i in range(len(s)): + map[ord(s[i])] += 1 + if map[ord(s[i])] % 2 == 0: + count -= 1 + else: + count += 1 + return count <= 1 +``` + +```java +class Solution { + public boolean canPermutePalindrome(String s) { + int[] map = new int[128]; + int count = 0; + for (int i = 0; i < s.length(); i++) { + map[s.charAt(i)]++; + if (map[s.charAt(i)] % 2 == 0) count--; + else count++; + } + return count <= 1; + } +} +``` + +```cpp +class Solution { +public: + bool canPermutePalindrome(string s) { + int map[128] = {0}; + int count = 0; + for (int i = 0; i < s.length(); i++) { + map[int(s[i])]++; + if (map[int(s[i])] % 2 == 0) + count--; + else + count++; + } + return count <= 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + canPermutePalindrome(s) { + const map = new Array(128).fill(0); + let count = 0; + for (let i = 0; i < s.length; i++) { + map[s.charCodeAt(i)]++; + if (map[s.charCodeAt(i)] % 2 === 0) count--; + else count++; + } + return count <= 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ If the we assume the input contains only ASCII characters. + - $O(k)$ If the implementation is modiifed to handle Unicode characters, the space complexity would depend on the number of unique characters in the string. $O(n)$ in the worst case (if all characters are unique). + +> Where $n$ is the size of the input string `s` and where $k$ is the number of unique characters in `s` + + +--- + +## 5. Using Set + +::tabs-start + +```python +class Solution: + def canPermutePalindrome(self, s: str) -> bool: + chars = set() + for c in s: + if c in chars: + chars.remove(c) + else: + chars.add(c) + return len(chars) <= 1 +``` + +```java +class Solution { + public boolean canPermutePalindrome(String s) { + Set set = new HashSet<>(); + for (int i = 0; i < s.length(); i++) { + if (!set.add(s.charAt(i))) set.remove(s.charAt(i)); + } + return set.size() <= 1; + } +} +``` + +```cpp +class Solution { +public: + bool canPermutePalindrome(string s) { + unordered_set char_set; + for (char c : s) { + if (char_set.find(c) != char_set.end()) + char_set.erase(c); + else + char_set.insert(c); + } + return char_set.size() <= 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + canPermutePalindrome(s) { + const set = new Set(); + for (let i = 0; i < s.length; i++) { + if (set.has(s.charAt(i))) { + set.delete(s.charAt(i)); + } else { + set.add(s.charAt(i)); + } + } + return set.size <= 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ If the we assume the input contains only ASCII characters. + - $O(k)$ If the implementation is modiifed to handle Unicode characters, the space complexity would depend on the number of unique characters in the string. $O(n)$ in the worst case (if all characters are unique) + +> Where $n$ is the size of the input string `s` and where $k$ is the number of unique characters in `s` From c5f7a2ad01bcec7d2d13831ea823c449d23129cd Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Fri, 14 Nov 2025 05:44:24 -0800 Subject: [PATCH 22/32] add sentence-similarity.md article --- articles/sentence-similarity.md | 139 ++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 articles/sentence-similarity.md diff --git a/articles/sentence-similarity.md b/articles/sentence-similarity.md new file mode 100644 index 000000000..84616ad43 --- /dev/null +++ b/articles/sentence-similarity.md @@ -0,0 +1,139 @@ +## 1. Using Hash Map and Hash Set + +::tabs-start + +```python +class Solution(object): + def areSentencesSimilar(self, sentence1, sentence2, similarPairs): + if len(sentence1) != len(sentence2): + return False + + wordToSimilarWords = defaultdict(set) + + for word1, word2 in similarPairs: + wordToSimilarWords[word1].add(word2) + wordToSimilarWords[word2].add(word1) + + for i in range(len(sentence1)): + if sentence1[i] == sentence2[i] or sentence2[i] in wordToSimilarWords[sentence1[i]]: + continue + return False + + return True +``` + +```java +class Solution { +public boolean areSentencesSimilar(String[] sentence1, String[] sentence2, List> similarPairs) { + if (sentence1.length != sentence2.length) { + return false; + } + + Map> wordToSimilarWords = new HashMap<>(); + + for (List pair : similarPairs) { + wordToSimilarWords.computeIfAbsent(pair.get(0), value->new HashSet()).add(pair.get(1)); + wordToSimilarWords.computeIfAbsent(pair.get(1), value->new HashSet()).add(pair.get(0)); + } + + for (int i = 0; i < sentence1.length; i++) { + // If the words are equal, continue. + if (sentence1[i].equals(sentence2[i])) { + continue; + } + + // If the words form a similar pair, continue. + if (wordToSimilarWords.containsKey(sentence1[i]) && + wordToSimilarWords.get(sentence1[i]).contains(sentence2[i])) { + continue; + } + return false; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool areSentencesSimilar(vector& sentence1, vector& sentence2, + vector>& similarPairs) { + if (sentence1.size() != sentence2.size()) { + return false; + } + unordered_map> wordToSimilarWords; + for (auto& pair : similarPairs) { + wordToSimilarWords[pair[0]].insert(pair[1]); + wordToSimilarWords[pair[1]].insert(pair[0]); + } + + for (int i = 0; i < sentence1.size(); i++) { + // If the words are equal, continue. + if (sentence1[i] == sentence2[i]) { + continue; + } + // If the words form a similar pair, continue. + if (wordToSimilarWords[sentence1[i]].count(sentence2[i])) { + continue; + } + return false; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} sentence1 + * @param {string[]} sentence2 + * @param {string[][]} similarPairs + * @return {boolean} + */ + areSentencesSimilar(sentence1, sentence2, similarPairs) { + if (sentence1.length !== sentence2.length) { + return false; + } + + const wordToSimilarWords = new Map(); + for (const pair of similarPairs) { + if (!wordToSimilarWords.has(pair[0])) { + wordToSimilarWords.set(pair[0], new Set()); + } + wordToSimilarWords.get(pair[0]).add(pair[1]); + + if (!wordToSimilarWords.has(pair[1])) { + wordToSimilarWords.set(pair[1], new Set()); + } + wordToSimilarWords.get(pair[1]).add(pair[0]); + } + + for (let i = 0; i < sentence1.length; i++) { + // If the words are equal, continue. + if (sentence1[i] === sentence2[i]) { + continue; + } + // If the words form a similar pair, continue. + if (wordToSimilarWords.has(sentence1[i]) && + wordToSimilarWords.get(sentence1[i]).has(sentence2[i])) { + continue; + } + return false; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O((n + k) \cdot m)$ +- Space complexity: $O(k\cdot m)$ + +> Where $n$ is the number of words in `sentence1` and `sentence2`, $k$ is the length of `similarPairs`, and $m$ is the average length of words in `sentence1` as well as `similarPairs`. From 5f9d08ed642ab6cd950885ed8198995d46a8726d Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Fri, 14 Nov 2025 08:08:57 -0800 Subject: [PATCH 23/32] add single-row-keyboard.md article --- articles/single-row-keyboard.md | 123 ++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 articles/single-row-keyboard.md diff --git a/articles/single-row-keyboard.md b/articles/single-row-keyboard.md new file mode 100644 index 000000000..7ae776bb2 --- /dev/null +++ b/articles/single-row-keyboard.md @@ -0,0 +1,123 @@ +## 1. Storing indexes for all letters + +::tabs-start + +```python +class Solution: + def calculateTime(self, keyboard: str, word: str) -> int: + key_indices = {} + # Get the index for each key. + for i in range(len(keyboard)): + key_indices[keyboard[i]] = i + + # Initialize previous index as starting index = 0. + prev = 0 + result = 0 + + # Calculate the total time. + for c in word: + # Add the distance from previous index + # to current letter's index to the result. + result += abs(prev - key_indices[c]) + + # Update the previous index to current index for next iteration. + prev = key_indices[c] + + return result +``` + +```java +class Solution { + public int calculateTime(String keyboard, String word) { + int[] keyIndices = new int[26]; + + // Get the index for each key. + for (int i = 0; i < keyboard.length(); i++) + keyIndices[keyboard.charAt(i) - 'a'] = i; + + // Initialize previous index as starting index = 0. + int prev = 0; + int result = 0; + + // Calculate the total time. + for (char c : word.toCharArray()) { + // Add the distance from previous index + // to current letter's index to the result. + result += Math.abs(prev - keyIndices[c - 'a']); + + // Update the previous index to current index for next iteration. + prev = keyIndices[c - 'a']; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + int calculateTime(string keyboard, string word) { + vector keyIndices(26, -1); + + // Get the index for each key. + for (int i = 0; i < keyboard.length(); i++) + keyIndices[keyboard[i] - 'a'] = i; + + // Initialize previous index as starting index = 0. + int prev = 0; + int result = 0; + + // Calculate the total time. + for (char &c : word) { + // Add the distance from previous index + // to current letter's index to the result. + result += abs(prev - keyIndices[c - 'a']); + + // Update the previous index to current index for next iteration. + prev = keyIndices[c - 'a']; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} keyboard + * @param {string} word + * @return {number} + */ + calculateTime(keyboard, word) { + const keyIndices = {}; + // Get the index for each key. + for (let i = 0; i < keyboard.length; i++) + keyIndices[keyboard[i]] = i; + + // Initialize previous index as starting index = 0. + let prev = 0; + let result = 0; + + // Calculate the total time. + for (const c of word) { + // Add the distance from previous index + // to current letter's index to the result. + result += Math.abs(prev - keyIndices[c]); + + // Update the previous index to current index for next iteration. + prev = keyIndices[c]; + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ constant space + +> Where $n$ is the length of `word`. From 438d24c563cd1254146b99fabfdabf508ed0d977 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sat, 15 Nov 2025 09:46:31 -0800 Subject: [PATCH 24/32] add group-shifted-strings.md article --- articles/group-shifted-strings.md | 138 ++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 articles/group-shifted-strings.md diff --git a/articles/group-shifted-strings.md b/articles/group-shifted-strings.md new file mode 100644 index 000000000..198f4777f --- /dev/null +++ b/articles/group-shifted-strings.md @@ -0,0 +1,138 @@ +## 1. Hashing + +::tabs-start + +```python +class Solution: + def groupStrings(self, strings: List[str]) -> List[List[str]]: + + def get_hash(string: str): + key = [] + for a, b in zip(string, string[1:]): + key.append(chr((ord(b) - ord(a)) % 26 + ord('a'))) + return ''.join(key) + + # Create a hash value (hash_key) for each string and append the string + # to the list of hash values i.e. mapHashToList["cd"] = ["acf", "gil", "xzc"] + groups = collections.defaultdict(list) + for string in strings: + hash_key = get_hash(string) + groups[hash_key].append(string) + + # Return a list of all of the grouped strings + return list(groups.values()) +``` + +```java +class Solution { + String getHash(String s) { + char[] chars = s.toCharArray(); + StringBuilder hashKey = new StringBuilder(); + + for (int i = 1; i < chars.length; i++) { + hashKey.append((char) ((chars[i] - chars[i - 1] + 26) % 26 + 'a')); + } + + return hashKey.toString(); + } + + public List> groupStrings(String[] strings) { + Map> mapHashToList = new HashMap<>(); + + // Create a hash_value (hashKey) for each string and append the string + // to the list of hash values i.e. mapHashToList["cd"] = ["acf", "gil", "xzc"] + for (String str : strings ) { + String hashKey = getHash(str); + if (mapHashToList.get(hashKey) == null) { + mapHashToList.put(hashKey, new ArrayList<>()); + } + mapHashToList.get(hashKey).add(str); + } + + // Iterate over the map, and add the values to groups + List> groups = new ArrayList<>(); + for (List group : mapHashToList.values()) { + groups.add(group); + } + + // Return a list of all of the grouped strings + return groups; + } +} +``` + +```cpp +class Solution { +public: + string getHash(string &s) { + string hashKey; + for (int i = 1; i < s.length(); i++) { + hashKey += (s[i] - s[i - 1] + 26) % 26 + 'a'; + } + + return hashKey; + } + + vector> groupStrings(vector& strings) { + unordered_map> mapHashToList; + + // Create a hash_value (hashKey) for each string and append the string + // to the list of hash values i.e. mapHashToList["cd"] = ["acf", "gil", "xzc"] + for (string str : strings) { + string hashKey = getHash(str); + mapHashToList[hashKey].push_back(str); + } + + // Iterate over the map, and add the values to groups + vector> groups; + for (auto it : mapHashToList) { + groups.push_back(it.second); + } + + // Return a list of all of the grouped strings + return groups; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strings + * @return {string[][]} + */ + groupStrings(strings) { + function getHash(string) { + const key = []; + for (let i = 0; i < string.length - 1; i++) { + const diff = (string.charCodeAt(i + 1) - string.charCodeAt(i) + 26) % 26; + key.push(String.fromCharCode(diff + 'a'.charCodeAt(0))); + } + return key.join(''); + } + + // Create a hash value (hash_key) for each string and append the string + // to the list of hash values i.e. mapHashToList["cd"] = ["acf", "gil", "xzc"] + const groups = new Map(); + for (const string of strings) { + const hashKey = getHash(string); + if (!groups.has(hashKey)) { + groups.set(hashKey, []); + } + groups.get(hashKey).push(string); + } + + // Return a list of all of the grouped strings + return Array.from(groups.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N \cdot K)$ +- Space complexity: $O(N \cdot K)$ + +> Where $N$ is the length of `strings` and $K$ is the maximum length of a string in `strings`. From c25bfb01dc519e5fa1e8ab75b6d614ac567ba58e Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sat, 15 Nov 2025 10:56:29 -0800 Subject: [PATCH 25/32] add largest-unique-number.md article --- articles/largest-unique-number.md | 401 ++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 articles/largest-unique-number.md diff --git a/articles/largest-unique-number.md b/articles/largest-unique-number.md new file mode 100644 index 000000000..8fd01d465 --- /dev/null +++ b/articles/largest-unique-number.md @@ -0,0 +1,401 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def largestUniqueNumber(self, nums: List[int]) -> int: + n = len(nums) + + # If there's only one element, it's unique by default + if n == 1: + return nums[0] + + nums.sort(reverse=True) + + # Start from the beginning (largest numbers) + currentIndex = 0 + + while currentIndex < n: + # If it's the first element or different from the next one, it's unique + if ( + currentIndex == n - 1 + or nums[currentIndex] != nums[currentIndex + 1] + ): + return nums[currentIndex] + # Skip duplicates + while ( + currentIndex < n - 1 + and nums[currentIndex] == nums[currentIndex + 1] + ): + currentIndex += 1 + # Move to the next unique number + currentIndex += 1 + + return -1 +``` + +```java +class Solution { + public int largestUniqueNumber(int[] nums) { + int n = nums.length; + + // If there's only one element, it's unique by default + if (n == 1) { + return nums[0]; + } + + Arrays.sort(nums); + + // Start from the end (largest numbers) + int currentIndex = n - 1; + + while (currentIndex >= 0) { + // If it's the first element or different from the previous one, it's unique + if ( + currentIndex == 0 || + nums[currentIndex] != nums[currentIndex - 1] + ) { + return nums[currentIndex]; + } + + // Skip duplicates + while ( + currentIndex > 0 && nums[currentIndex] == nums[currentIndex - 1] + ) { + currentIndex--; + } + + // Move to the next unique number + currentIndex--; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int largestUniqueNumber(vector& nums) { + int n = nums.size(); + + // If there's only one element, it's unique by default + if (n == 1) { + return nums[0]; + } + + sort(nums.begin(), nums.end(), greater()); + + // Start from the beginning (largest numbers) + int currentIndex = 0; + + while (currentIndex < n) { + // If it's the last element or different from the next one, + // it's unique + if (currentIndex == n - 1 || + nums[currentIndex] != nums[currentIndex + 1]) { + return nums[currentIndex]; + } + + // Skip duplicates + while (currentIndex < n - 1 && + nums[currentIndex] == nums[currentIndex + 1]) { + currentIndex++; + } + + // Move to the next unique number + currentIndex++; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestUniqueNumber(nums) { + const n = nums.length; + + // If there's only one element, it's unique by default + if (n === 1) { + return nums[0]; + } + + nums.sort((a, b) => b - a); + + // Start from the beginning (largest numbers) + let currentIndex = 0; + + while (currentIndex < n) { + // If it's the first element or different from the next one, it's unique + if ( + currentIndex === n - 1 + || nums[currentIndex] !== nums[currentIndex + 1] + ) { + return nums[currentIndex]; + } + + // Skip duplicates + while ( + currentIndex < n - 1 + && nums[currentIndex] === nums[currentIndex + 1] + ) { + currentIndex++; + } + + // Move to the next unique number + currentIndex++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \log n)$ +- Space complexity: $O(S)$ Depends on the language of implementation + +> Where $n$ is the length of the `nums` array and $S$ is the sorting algorthm + +--- + +## 2. Sorted Map + +::tabs-start + +```python +class Solution: + def largestUniqueNumber(self, nums: List[int]) -> int: + # Create a frequency map + frequency_map = {} + for num in nums: + frequency_map[num] = frequency_map.get(num, 0) + 1 + + # Create a sorted OrderedDict + sorted_map = OrderedDict(sorted(frequency_map.items(), reverse=True)) + + # Find the largest unique number + for num, freq in sorted_map.items(): + if freq == 1: + return num + + return -1 +``` + +```java +class Solution { + public int largestUniqueNumber(int[] nums) { + // Use a TreeMap to store numbers and their frequencies + TreeMap frequencyMap = new TreeMap<>(); + + // Populate the frequencyMap + for (int num : nums) { + frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1); + } + + // Initialize the result to -1 (default if no unique number is found) + int largestUnique = -1; + + // Iterate through the map in reverse order (largest to smallest) + for (Integer num : frequencyMap.descendingKeySet()) { + // If the frequency is 1, we've found our largest unique number + if (frequencyMap.get(num) == 1) { + largestUnique = num; + break; + } + } + + return largestUnique; + } +} +``` + +```cpp +class Solution { +public: + int largestUniqueNumber(vector& nums) { + // Use a map to store numbers and their frequencies + map frequencyMap; + + // Populate the frequencyMap + for (int num : nums) { + frequencyMap[num]++; + } + + // Initialize the result to -1 (default if no unique number is found) + int largestUnique = -1; + + // Iterate through the map in reverse order (largest to smallest) + for (auto it = frequencyMap.rbegin(); it != frequencyMap.rend(); ++it) { + // If the frequency is 1, we've found our largest unique number + if (it->second == 1) { + largestUnique = it->first; + break; + } + } + + return largestUnique; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestUniqueNumber(nums) { + // Create a frequency map + const frequencyMap = {}; + for (const num of nums) { + frequencyMap[num] = (frequencyMap[num] || 0) + 1; + } + + // Create a sorted OrderedDict + const sortedMap = new Map( + Object.entries(frequencyMap).sort((a, b) => b[0] - a[0]) + ); + + // Find the largest unique number + for (const [num, freq] of sortedMap) { + if (freq === 1) { + return Number(num); + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \log n)$ +- Space complexity: $O(n)$ + + +> Where $n$ is the length of the `nums` array. + +--- + +## 3. Map + +::tabs-start + +```python +class Solution: + def largestUniqueNumber(self, nums: List[int]) -> int: + # Use Counter to count frequencies of numbers + frequency_map = Counter(nums) + + # Find the largest number with frequency 1, or -1 if none found + return max( + (num for num, freq in frequency_map.items() if freq == 1), + default=-1, + ) +``` + +```java +class Solution { + public int largestUniqueNumber(int[] nums) { + // Create a HashMap to store the frequency of each number + Map frequencyMap = new HashMap<>(); + + // Populate the frequencyMap + for (int num : nums) { + frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1); + } + + // Initialize the result to -1 (default if no unique number is found) + int largestUnique = -1; + + for (int num : frequencyMap.keySet()) { + // Check if the number appears only once and is larger than the current largestUnique + if (frequencyMap.get(num) == 1 && num > largestUnique) { + largestUnique = num; + } + } + + return largestUnique; + } +} +``` + +```cpp +class Solution { +public: + int largestUniqueNumber(vector& nums) { + // Create an unordered_map to store the frequency of each number + unordered_map frequencyMap; + + // Populate the frequencyMap + for (int num : nums) { + frequencyMap[num]++; + } + + // Initialize the result to -1 (default if no unique number is found) + int largestUnique = -1; + + for (auto& pair : frequencyMap) { + // Check if the number appears only once and is larger than the + // current largestUnique + if (pair.second == 1 && pair.first > largestUnique) { + largestUnique = pair.first; + } + } + + return largestUnique; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestUniqueNumber(nums) { + // Create a HashMap to store the frequency of each number + const frequencyMap = new Map(); + + // Populate the frequencyMap + for (const num of nums) { + frequencyMap.set(num, (frequencyMap.get(num) || 0) + 1); + } + + // Initialize the result to -1 (default if no unique number is found) + let largestUnique = -1; + for (const num of frequencyMap.keys()) { + // Check if the number appears only once and is larger than the current largestUnique + if (frequencyMap.get(num) === 1 && num > largestUnique) { + largestUnique = num; + } + } + return largestUnique; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the length of the `nums` array. From abd5fd6331efeec56ecd2b52cd1adecd784bed03 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sun, 16 Nov 2025 08:33:33 -0800 Subject: [PATCH 26/32] add counting-elements.md article --- articles/counting-elements.md | 262 ++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 articles/counting-elements.md diff --git a/articles/counting-elements.md b/articles/counting-elements.md new file mode 100644 index 000000000..d87203400 --- /dev/null +++ b/articles/counting-elements.md @@ -0,0 +1,262 @@ +## 1. Search with Array + +::tabs-start + +```python +class Solution: + def countElements(self, arr: List[int]) -> int: + count = 0 + for x in arr: + if x + 1 in arr: + count += 1 + return count +``` + +```java +class Solution { + public int countElements(int[] arr) { + int count = 0; + for (int x : arr) { + if (integerInArray(arr, x + 1)) { + count++; + } + } + return count; + } + + public boolean integerInArray(int[] arr, int target) { + for (int x : arr) { + if (x == target) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + int countElements(vector& arr) { + int count = 0; + for (auto x : arr) { + if (integerInArray(arr, x + 1)) { + count++; + } + } + return count; + } + + bool integerInArray(vector& arr, int target) { + for (auto x : arr) { + if (x == target) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countElements(arr) { + let count = 0; + for (const x of arr) { + if (arr.includes(x + 1)) { + count++; + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N^2)$ +- Space complexity: $O(1)$ constant space + +> Where $N$ is the length of the input array `arr`. + +--- + +## 2. Search with HashSet + +::tabs-start + +```python +class Solution: + def countElements(self, arr: List[int]) -> int: + hash_set = set(arr) + count = 0 + for x in arr: + if x + 1 in hash_set: + count += 1 + return count +``` + +```java +class Solution { + public int countElements(int[] arr) { + Set hashSet = new HashSet<>(); + for (int x : arr) { + hashSet.add(x); + } + int count = 0; + for (int x : arr) { + if (hashSet.contains(x + 1)) { + count++; + } + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int countElements(vector& arr) { + unordered_set hashSet(arr.begin(), arr.end()); + int count = 0; + for (int x : arr) { + if (hashSet.find(x + 1) != hashSet.end()) { + count++; + } + } + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countElements(arr) { + const hashSet = new Set(arr); + let count = 0; + for (const x of arr) { + if (hashSet.has(x + 1)) { + count++; + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the length of the input array `arr`. + +--- + +## 3. Search with Sorted Array + +::tabs-start + +```python +class Solution: + def countElements(self, arr: List[int]) -> int: + arr.sort() + count = 0 + run_length = 1 + for i in range(1, len(arr)): + if arr[i - 1] != arr[i]: + if arr[i - 1] + 1 == arr[i]: + count += run_length + run_length = 0 + run_length += 1 + return count +``` + +```java +class Solution { + public int countElements(int[] arr) { + Arrays.sort(arr); + int count = 0; + int runLength = 1; + for (int i = 1; i < arr.length; i++) { + if (arr[i - 1] != arr[i]) { + if (arr[i - 1] + 1 == arr[i]) { + count += runLength; + } + runLength = 0; + } + runLength++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int countElements(vector& arr) { + std::sort(arr.begin(), arr.end()); + int count = 0; + int runLength = 1; + for (int i = 1; i < arr.size(); i++) { + if (arr[i - 1] != arr[i]) { + if (arr[i - 1] + 1 == arr[i]) { + count += runLength; + } + runLength = 0; + } + runLength++; + } + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countElements(arr) { + arr.sort((a, b) => a - b); + let count = 0; + let runLength = 1; + for (let i = 1; i < arr.length; i++) { + if (arr[i - 1] !== arr[i]) { + if (arr[i - 1] + 1 === arr[i]) { + count += runLength; + } + runLength = 0; + } + runLength++; + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N \log N)$ +- Space complexity: varies from $O(N)$ to $O(1)$ + - The overall space complexity is dependent on the space complexity of the sorting algorithm you're using. The space complexity of sorting algorithms built into programming languages are generally anywhere from $O(N)$ to $O(1)$. + +> Where $N$ is the length of the input array `arr`. From 2de178b06b5e57131bc2d18353eb32026d6d5e7c Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 17 Nov 2025 06:27:12 -0800 Subject: [PATCH 27/32] add find-smallest-common-element-in-all-rows.md article --- ...ind-smallest-common-element-in-all-rows.md | 580 ++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 articles/find-smallest-common-element-in-all-rows.md diff --git a/articles/find-smallest-common-element-in-all-rows.md b/articles/find-smallest-common-element-in-all-rows.md new file mode 100644 index 000000000..981b3ad42 --- /dev/null +++ b/articles/find-smallest-common-element-in-all-rows.md @@ -0,0 +1,580 @@ +## 1. Count Elements + +::tabs-start + +```python +class Solution: + def smallestCommonElement(self, mat: List[List[int]]) -> int: + count = [0] * 10001 + n, m = len(mat), len(mat[0]) + + for i in range(n): + for j in range(m): + count[mat[i][j]] += 1 + + for k in range(1, 10001): + if count[k] == n: + return k + + return -1 +``` + +```java +class Solution { + public int smallestCommonElement(int[][] mat) { + int count[] = new int[10001]; + int n = mat.length, m = mat[0].length; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + ++count[mat[i][j]]; + } + } + for (int k = 1; k <= 10000; ++k) { + if (count[k] == n) { + return k; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int smallestCommonElement(vector>& mat) { + int count[10001] = {}; + int n = mat.size(), m = mat[0].size(); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + ++count[mat[i][j]]; + } + } + for (int k = 1; k <= 10000; ++k) { + if (count[k] == n) { + return k; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + smallestCommonElement(mat) { + const count = new Array(10001).fill(0); + const n = mat.length, m = mat[0].length; + + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + count[mat[i][j]]++; + } + } + + for (let k = 1; k <= 10000; k++) { + if (count[k] === n) { + return k; + } + } + + return -1; + } +} +``` + +::tabs-end + +## 1. Count Elements (Improved) + +We can improve the average time complexity if we count elements column-by-column. This way, smaller elements will be counted first, and we can exit as soon as we get to an element that repeats `n` times. + +::tabs-start + +```python +class Solution: + def smallestCommonElement(self, mat: List[List[int]]) -> int: + count = [0] * 10001 + n, m = len(mat), len(mat[0]) + + for j in range(m): + for i in range(n): + count[mat[i][j]] += 1 + if count[mat[i][j]] == n: + return mat[i][j] + + return -1 +``` + +```java +class Solution { + public int smallestCommonElement(int[][] mat) { + int count[] = new int[10001]; + int n = mat.length, m = mat[0].length; + for (int j = 0; j < m; ++j) { + for (int i = 0; i < n; ++i) { + if (++count[mat[i][j]] == n) { + return mat[i][j]; + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int smallestCommonElement(vector>& mat) { + int count[10001] = {}; + int n = mat.size(), m = mat[0].size(); + for (int j = 0; j < m; ++j) { + for (int i = 0; i < n; ++i) { + if (++count[mat[i][j]] == n) { + return mat[i][j]; + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + smallestCommonElement(mat) { + const count = new Array(10001).fill(0); + const n = mat.length, m = mat[0].length; + + for (let j = 0; j < m; j++) { + for (let i = 0; i < n; i++) { + count[mat[i][j]]++; + if (count[mat[i][j]] === n) { + return mat[i][j]; + } + } + } + + return -1; + } +} +``` + +::tabs-end + +## Handling Duplicates + +If elements are in non-decreasing order, we'll need to modify these solutions to properly handle duplicates. For example, we return `4` (initial solution) and `7` (improved solution) instead of `5` for this test case: + +`[[1,2,3,4,5],[5,7,7,7,7],[5,7,7,7,7],[1,2,4,4,5],[1,2,4,4,5]]` + +It's easy to modify these solutions to handle duplicates. Since elements in a row are sorted, we can skip the current element if it's equal to the previous one. + +### Time & Space Complexity +- Time complexity: $O(nm)$ +- Space complexity: + - Constrained problem: $O(10000) = O(1)$ constant space. + - Unconstrained problem: $O(k)$, where $k$ is the number of unique elements. + +> where $m$ is the number of rows and $n$ is the number of columns + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def smallestCommonElement(self, mat: List[List[int]]) -> int: + n, m = len(mat), len(mat[0]) + + for j in range(m): + found = True + i = 1 + while i < n and found: + found = self.binarySearch(mat[i], mat[0][j]) >= 0 + i += 1 + + if found: + return mat[0][j] + + return -1 + + + def binarySearch(self, arr, target): + left, right = 0, len(arr) - 1 + while left <= right: + mid = (left + right) // 2 + if arr[mid] == target: + return mid + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + return -1 +``` + +```java +class Solution { + public int smallestCommonElement(int[][] mat) { + int n = mat.length, m = mat[0].length; + for (int j = 0; j < m; ++j) { + boolean found = true; + for (int i = 1; i < n && found; ++i) { + found = Arrays.binarySearch(mat[i], mat[0][j]) >= 0; + } + if (found) { + return mat[0][j]; + } + } + return -1; + } +} +``` + +```cpp +class Solution { + int smallestCommonElement(vector>& mat) { + int n = mat.size(), m = mat[0].size(); + for (int j = 0; j < m; ++j) { + bool found = true; + for (int i = 1; i < n && found; ++i) { + found = binary_search(begin(mat[i]), end(mat[i]), mat[0][j]); + } + if (found) { + return mat[0][j]; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + smallestCommonElement(mat) { + const n = mat.length, m = mat[0].length; + + for (let j = 0; j < m; j++) { + let found = true; + for (let i = 1; i < n && found; i++) { + found = this.binarySearch(mat[i], mat[0][j]) >= 0; + } + if (found) { + return mat[0][j]; + } + } + + return -1; + } + + /** + * @param {number[]} arr + * @param {number} target + * @return {number} + */ + binarySearch(arr, target) { + let left = 0, right = arr.length - 1; + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (arr[mid] === target) { + return mid; + } else if (arr[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +} +``` + +::tabs-end + +## 2. Binary Search (Improved) + +In the solution above, we always search the entire row. We can improve the average time complexity if we start the next search from the position returned by the previous search. We can also return `-1` if all elements in the row are smaller than value we searched for. + +::tabs-start + +```python +class Solution: + def smallestCommonElement(self, mat: List[List[int]]) -> int: + n, m = len(mat), len(mat[0]) + pos = [0] * n + + for j in range(m): + found = True + i = 1 + while i < n and found: + pos[i] = self.binarySearch(mat[i], pos[i], m, mat[0][j]) + if pos[i] < 0: + found = False + pos[i] = -pos[i] - 1 + if pos[i] >= m: + return -1 + i += 1 + + if found: + return mat[0][j] + + return -1 + + + def binarySearch(self, arr, fromIndex, toIndex, target): + left, right = fromIndex, toIndex - 1 + while left <= right: + mid = (left + right) // 2 + if arr[mid] == target: + return mid + elif arr[mid] < target: + left = mid + 1 + else: + right = mid - 1 + return -(left + 1) +``` + +```java +class Solution { + public int smallestCommonElement(int[][] mat) { + int n = mat.length, m = mat[0].length; + int pos[] = new int[n]; + for (int j = 0; j < m; ++j) { + boolean found = true; + for (int i = 1; i < n && found; ++i) { + pos[i] = Arrays.binarySearch(mat[i], pos[i], m, mat[0][j]); + if (pos[i] < 0) { + found = false; + pos[i] = -pos[i] - 1; + if (pos[i] >= m) { + return -1; + } + } + } + if (found) { + return mat[0][j]; + } + } + return -1; + } +} +``` + +```cpp +class Solution { + int smallestCommonElement(vector>& mat) { + int n = mat.size(), m = mat[0].size(); + vector pos(n); + for (int j = 0; j < m; ++j) { + bool found = true; + for (int i = 1; i < n && found; ++i) { + pos[i] = lower_bound(begin(mat[i]) + pos[i], end(mat[i]), mat[0][j]) - begin(mat[i]); + if (pos[i] >= m) { + return -1; + } + found = mat[i][pos[i]] == mat[0][j]; + } + if (found) { + return mat[0][j]; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + smallestCommonElement(mat) { + const n = mat.length, m = mat[0].length; + const pos = new Array(n).fill(0); + + for (let j = 0; j < m; j++) { + let found = true; + for (let i = 1; i < n && found; i++) { + pos[i] = this.binarySearch(mat[i], pos[i], m, mat[0][j]); + if (pos[i] < 0) { + found = false; + pos[i] = -pos[i] - 1; + if (pos[i] >= m) { + return -1; + } + } + } + if (found) { + return mat[0][j]; + } + } + + return -1; + } + + /** + * @param {number[]} arr + * @param {number} fromIndex + * @param {number} toIndex + * @param {number} target + * @return {number} + */ + binarySearch(arr, fromIndex, toIndex, target) { + let left = fromIndex, right = toIndex - 1; + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (arr[mid] === target) { + return mid; + } else if (arr[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -(left + 1); + } +} +``` + +::tabs-end + +## Handling Duplicates + +Since we search for an element in each row, this approach works correctly if there are duplicates. + +### Time & Space Complexity + +- Time complexity: $O(m n \log m)$ +- Space complexity: + - Original Solution: $O(1)$ constant space. + - Improved Solution: $O(n)$ + +> where $m$ is the number of rows and $n$ is the number of columns + +--- + +## 3. Row Positions + +::tabs-start + +```python +class Solution: + def smallestCommonElement(self, mat: List[List[int]]) -> int: + +``` + +```java +class Solution { + public int smallestCommonElement(int[][] mat) { + int n = mat.length, m = mat[0].length; + int pos[] = new int[n], cur_max = 0, cnt = 0; + while (true) { + for (int i = 0; i < n; ++i) { + while (pos[i] < m && mat[i][pos[i]] < cur_max) { + ++pos[i]; + } + if (pos[i] >= m) { + return -1; + } + if (mat[i][pos[i]] != cur_max) { + cnt = 1; + cur_max = mat[i][pos[i]]; + } else if (++cnt == n) { + return cur_max; + } + } + } + } +} +``` + +```cpp +class Solution { +public: + int smallestCommonElement(vector>& mat) { + int n = mat.size(), m = mat[0].size(); + int cur_max = 0, cnt = 0; + vector pos(n); + while (true) { + for (int i = 0; i < n; ++i) { + while (pos[i] < m && mat[i][pos[i]] < cur_max) { + ++pos[i]; + } + if (pos[i] >= m) { + return -1; + } + if (cur_max != mat[i][pos[i]]) { + cnt = 1; + cur_max = mat[i][pos[i]]; + } else if (++cnt == n) { + return cur_max; + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + smallestCommonElement(mat) { + const n = mat.length, m = mat[0].length; + const pos = new Array(n).fill(0); + let cur_max = 0, cnt = 0; + + while (true) { + for (let i = 0; i < n; i++) { + while (pos[i] < m && mat[i][pos[i]] < cur_max) { + pos[i]++; + } + if (pos[i] >= m) { + return -1; + } + if (mat[i][pos[i]] !== cur_max) { + cnt = 1; + cur_max = mat[i][pos[i]]; + } else { + cnt++; + if (cnt === n) { + return cur_max; + } + } + } + } + } +} +``` + +::tabs-end + +## Handling Duplicates + +Since we take one element from each row, this approach works correctly if there are duplicates. + +### Time & Space Complexity + +- Time complexity: $O(nm)$ +- Space complexity: $O(n)$ + +> where $m$ is the number of rows and $n$ is the number of columns From 7bfb90782e9a05227ffaf253706b7686a01b2afd Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 17 Nov 2025 09:14:51 -0800 Subject: [PATCH 28/32] add valid-word-square.md article --- articles/valid-word-square.md | 258 ++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 articles/valid-word-square.md diff --git a/articles/valid-word-square.md b/articles/valid-word-square.md new file mode 100644 index 000000000..4481c99ac --- /dev/null +++ b/articles/valid-word-square.md @@ -0,0 +1,258 @@ +## 1. Storing New Words + +::tabs-start + +```python +class Solution: + def validWordSquare(self, words: List[str]) -> bool: + cols = 0 + rows = len(words) + new_words = [] + + for word in words: + cols = max(cols, len(word)) + + # If the first row doesn't have maximum number of characters, or + # the number of rows is not equal to columns it can't form a square. + if cols != len(words[0]) or rows != cols: + return False + + for col in range(cols): + new_word = [] + # Iterate on each character of column 'col'. + for row in range(rows): + # If the current row's word's size is less than the column number it means this column is empty, + # or, if there is a character present then use it to make the new word. + if col < len(words[row]): + new_word.append(words[row][col]) + # Push the new word of column 'col' in the list. + new_words.append(''.join(new_word)) + + # Check if all row's words match with the respective column's words. + return words == new_words +``` + +```java +class Solution { + public boolean validWordSquare(List words) { + int cols = 0; + int rows = words.size(); + List newWords = new ArrayList(); + + for (String word : words) { + cols = Math.max(cols, word.length()); + } + + // If the first row doesn't have maximum number of characters, or + // the number of rows is not equal to columns it can't form a square. + if (cols != words.get(0).length() ||rows != cols) { + return false; + } + + for (int col = 0; col < cols; ++col) { + StringBuilder newWord = new StringBuilder(); + // Iterate on each character of column 'col'. + for (int row = 0; row < rows; ++row) { + // If the current row's word's size is less than the column number it means this column is empty, + // or, if there is a character present then use it to make the new word. + if (col < words.get(row).length()) { + newWord.append(words.get(row).charAt(col)); + } + } + // Push the new word of column 'col' in the list. + newWords.add(newWord.toString()); + } + + // Check if all row's words match with the respective column's words. + for (int index = 0; index < rows; ++index) { + if (words.get(index).compareTo(newWords.get(index)) != 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validWordSquare(vector& words) { + int cols = 0; + int rows = words.size(); + vector newWords; + + for (auto& word : words) { + cols = max(cols, (int)word.size()); + } + + // If the first row doesn't have maximum number of characters, or + // the number of rows is not equal to columns it can't form a square. + if (cols != words[0].size() || rows != cols) { + return false; + } + + for (int col = 0; col < cols; ++col) { + string newWord; + // Iterate on each character of column 'col'. + for (int row = 0; row < rows; ++row) { + // If the current row's word's size is less than the column number it means this column is empty, + // or, if there is a character present then use it to make the new word. + if (col < words[row].size()) { + newWord += words[row][col]; + } + } + // Push the new word of column 'col' in the list. + newWords.push_back(newWord); + } + + // Check if all row's words match with the respective column's words. + return words == newWords; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + validWordSquare(words) { + let cols = 0; + let rows = words.length; + let newWords = []; + + for (let word of words) { + cols = Math.max(cols, word.length); + } + + // If the first row doesn't have maximum number of characters, or + // the number of rows is not equal to columns it can't form a square. + if (cols != words[0].length || rows != cols) { + return false; + } + + for (let col = 0; col < cols; ++col) { + let newWord = ""; + // Iterate on each character of column 'col'. + for (let row = 0; row < rows; ++row) { + // If the current row's word's size is less than the column number it means this column is empty, + // or, if there is a character present then use it to make the new word. + if (col < words[row].length) { + newWord += words[row][col]; + } + } + // Push the new word of column 'col' in the list. + newWords.push(newWord); + } + + // Check if all row's words match with the respective column's words. + return words.every((value, index) => value === newWords[index]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot m)$ +- Space complexity: $O(n \cdot m)$ + +> Where $n$ is the number of strings in the `words` array and $m$ is the maximum length of a string + +--- + +## 2. Iterate on the Matrix + +::tabs-start + +```python +class Solution: + def validWordSquare(self, words: List[str]) -> bool: + for word_num in range(len(words)): + for char_pos in range(len(words[word_num])): + # char_pos (curr 'row' word) is bigger than column word, or + # word_num (curr 'column' word) is bigger than row word, or + # characters at index (word_num,char_pos) and (char_pos,word_num) are not equal. + if char_pos >= len(words) or \ + word_num >= len(words[char_pos]) or \ + words[word_num][char_pos] != words[char_pos][word_num]: + return False + return True +``` + +```java +class Solution { + public boolean validWordSquare(List words) { + for (int wordNum = 0; wordNum < words.size(); ++wordNum) { + for (int charPos = 0; charPos < words.get(wordNum).length(); ++charPos) { + // charPos (curr 'row' word) is bigger than column word, or + // wordNum (curr 'column' word) is bigger than row word, or + // characters at index (wordNum,charPos) and (charPos,wordNum) are not equal. + if (charPos >= words.size() || + wordNum >= words.get(charPos).length() || + words.get(wordNum).charAt(charPos) != words.get(charPos).charAt(wordNum)){ + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validWordSquare(vector& words) { + for (int wordNum = 0; wordNum < words.size(); ++wordNum) { + for (int charPos = 0; charPos < words[wordNum].size(); ++charPos) { + // charPos (curr 'row' word) is bigger than column word, or + // wordNum (curr 'column' word) is bigger than row word, or + // characters at index (wordNum,charPos) and (charPos,wordNum) are not equal. + if (charPos >= words.size() || + wordNum >= words[charPos].size() || + words[wordNum][charPos] != words[charPos][wordNum]){ + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + validWordSquare(words) { + for (let wordNum = 0; wordNum < words.length; ++wordNum) { + for (let charPos = 0; charPos < words[wordNum].length; ++charPos) { + // charPos (curr 'row' word) is bigger than column word, or + // wordNum (curr 'column' word) is bigger than row word, or + // characters at index (wordNum,charPos) and (charPos,wordNum) are not equal. + if (charPos >= words.length || + wordNum >= words[charPos].length || + words[wordNum][charPos] != words[charPos][wordNum]){ + return false; + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot m)$ +- Space complexity: $O(1)$ constant space + +> Where $n$ is the number of strings in the `words` array and $m$ is the maximum length of a string From 1faa3a3fd831a85e16c5bfd2e358ae774c9ee556 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 17 Nov 2025 10:02:40 -0800 Subject: [PATCH 29/32] add lonely-pixel-i.md article --- articles/lonely-pixel-i.md | 423 +++++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 articles/lonely-pixel-i.md diff --git a/articles/lonely-pixel-i.md b/articles/lonely-pixel-i.md new file mode 100644 index 000000000..21bc47173 --- /dev/null +++ b/articles/lonely-pixel-i.md @@ -0,0 +1,423 @@ +## 1. Counting with Arrays + +::tabs-start + +```python +class Solution: + def findLonelyPixel(self, picture: List[List[str]]) -> int: + n = len(picture) + m = len(picture[0]) + + # Arrays to store the count of black cells in rows and columns. + row_count = [0] * n + column_count = [0] * m + for i in range(n): + for j in range(m): + if picture[i][j] == 'B': + row_count[i] += 1 + column_count[j] += 1 + + answer = 0 + for i in range(n): + for j in range(m): + # Its a lonely cell, if the current cell is black and, + # the count of black cells in its row and column is 1. + if picture[i][j] == 'B' and row_count[i] == 1 and column_count[j] == 1: + answer += 1 + + return answer +``` + +```java +class Solution { + public int findLonelyPixel(char[][] picture) { + int n = picture.length; + int m = picture[0].length; + + // Arrays to store the count of black cells in rows and columns. + int rowCount[] = new int[n]; + int columnCount[] = new int[m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (picture[i][j] == 'B') { + rowCount[i]++; + columnCount[j]++; + } + } + } + + int answer = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + // Its a lonely cell, if the current cell is black and, + // the count of black cells in its row and column is 1. + if (picture[i][j] == 'B' && rowCount[i] == 1 && columnCount[j] == 1) { + answer++; + } + } + } + + return answer; + } +} +``` + +```cpp +class Solution { +public: + int findLonelyPixel(vector>& picture) { + int n = int(picture.size()); + int m = int(picture[0].size()); + + // Arrays to store the count of black cells in rows and columns. + vector rowCount(n, 0); + vector columnCount(m, 0); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (picture[i][j] == 'B') { + rowCount[i]++; + columnCount[j]++; + } + } + } + + int answer = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + // Its a lonely cell, if the current cell is black and, + // the count of black cells in its row and column is 1. + if (picture[i][j] == 'B' && rowCount[i] == 1 && columnCount[j] == 1) { + answer++; + } + } + } + + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} picture + * @return {number} + */ + findLonelyPixel(picture) { + let n = picture.length; + let m = picture[0].length; + + // Arrays to store the count of black cells in rows and columns. + let rowCount = new Array(n).fill(0); + let columnCount = new Array(m).fill(0); + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + if (picture[i][j] === 'B') { + rowCount[i]++; + columnCount[j]++; + } + } + } + + let answer = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + // Its a lonely cell, if the current cell is black and, + // the count of black cells in its row and column is 1. + if (picture[i][j] === 'B' && rowCount[i] === 1 && columnCount[j] === 1) { + answer++; + } + } + } + + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(M \cdot N)$ +- Space complexity: $O(M + N)$ + +> Where $M$ is the number of rows in the given matrix `picture`, and $N$ is the number of columns in it. + +--- + +## 2. Space Optimized Counting + +::tabs-start + +```python +class Solution: + def findLonelyPixel(self, picture: List[List[str]]) -> int: + # Returns true if the cell at (x, y) is lonely. + # There should not be any other black cell + # In the first row and column except (x, y) itself. + def check(x, y): + n = len(picture) + m = len(picture[0]) + + cnt = 0 + for i in range(n): + cnt += 1 if picture[i][y] == 'B' else 0 + + for j in range(m): + # avoid double count (x, y) + if j != y: + cnt += 1 if picture[x][j] == 'B' else 0 + + return picture[x][y] == 'B' and cnt == 1 + + n = len(picture) + m = len(picture[0]) + + answer = 0 + for j in range(m): + answer += 1 if check(0, j) else 0 + + for i in range(1, n): + answer += 1 if check(i, 0) else 0 + + # Convert cell 'B' to '1' and 'W' to '0' + for j in range(m): + picture[0][j] = '1' if picture[0][j] == 'B' else '0' + + for i in range(n): + picture[i][0] = '1' if picture[i][0] == 'B' else '0' + + # If the cell is black increment the count of corresponding row and column by 1 + for i in range(1, n): + for j in range(1, m): + if picture[i][j] == 'B': + picture[i][0] = chr(ord(picture[i][0]) + 1) + picture[0][j] = chr(ord(picture[0][j]) + 1) + + for i in range(1, n): + for j in range(1, m): + if picture[i][j] == 'B': + if picture[0][j] == '1' and picture[i][0] == '1': + answer += 1 + + return answer +``` + +```java +class Solution { + + // Returns true if the cell at (x, y) is lonely. + // There should not be any other black cell + // In the first row and column except (x, y) itself. + boolean check(char[][] picture, int x, int y) { + int n = picture.length; + int m = picture[0].length; + + int cnt = 0; + for (int i = 0; i < n; i++) { + cnt += (picture[i][y] == 'B' ? 1 : 0); + } + + for (int j = 0; j < m; j++) { + // avoid double count (x, y) + if (j != y) cnt += (picture[x][j] == 'B' ? 1 : 0); + } + return picture[x][y] == 'B' && cnt == 1; + } + + public int findLonelyPixel(char[][] picture) { + int n = picture.length; + int m = picture[0].length; + + int answer = 0; + for (int j = 0; j < m; j++) { + answer += check(picture, 0, j) ? 1 : 0; + } + for (int i = 1; i < n; i++) { + answer += check(picture, i, 0) ? 1 : 0; + } + + // Convert cell 'B' to '1' and 'W' to '0' + for (int j = 0; j < m; j++) { + picture[0][j] = (picture[0][j] == 'B' ? '1' : '0'); + } + + for (int i = 0; i < n; i++) { + picture[i][0] = (picture[i][0] == 'B' ? '1' : '0'); + } + + // If the cell is black increment the count of corresponding row and column by 1 + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + if (picture[i][j] == 'B') { + picture[i][0]++; + picture[0][j]++; + } + } + } + + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + if (picture[i][j] == 'B') { + if (picture[0][j] == '1' && picture[i][0] == '1') { + answer++; + } + } + } + } + + return answer; + } +} +``` + +```cpp + +class Solution { +public: + // Returns true if the cell at (x, y) is lonely. + // There should not be any other black cell + // In the first row and column except (x, y) itself. + bool check(vector>& picture, int x, int y) { + int n = int(picture.size()); + int m = int(picture[0].size()); + + int cnt = 0; + for (int i = 0; i < n; i++) { + cnt += (picture[i][y] == 'B'); + } + + for (int j = 0; j < m; j++) { + // avoid double count (x, y) + if (j != y) cnt += (picture[x][j] == 'B'); + } + return picture[x][y] == 'B' && cnt == 1; + } + + int findLonelyPixel(vector>& picture) { + int n = int(picture.size()); + int m = int(picture[0].size()); + + int answer = 0; + // Lonely cells in the first row + for (int j = 0; j < m; j++) { + answer += check(picture, 0, j); + } + //Lonely cells in the first column + for (int i = 1; i < n; i++) { + answer += check(picture, i, 0); + } + + // Convert cell 'B' to '1' and 'W' to '0' + for (int j = 0; j < m; j++) { + picture[0][j] = (picture[0][j] == 'B' ? '1' : '0'); + } + + for (int i = 0; i < n; i++) { + picture[i][0] = (picture[i][0] == 'B' ? '1' : '0'); + } + + // If the cell is black increment the count of corresponding row and column by 1 + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + if (picture[i][j] == 'B') { + picture[i][0]++; + picture[0][j]++; + } + } + } + + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + if (picture[i][j] == 'B') { + if (picture[0][j] == '1' && picture[i][0] == '1') { + answer++; + } + } + } + } + + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} picture + * @return {number} + */ + findLonelyPixel(picture) { + // Returns true if the cell at (x, y) is lonely. + // There should not be any other black cell + // In the first row and column except (x, y) itself. + const check = (x, y) => { + let n = picture.length; + let m = picture[0].length; + + let cnt = 0; + for (let i = 0; i < n; i++) { + cnt += (picture[i][y] === 'B' ? 1 : 0); + } + + for (let j = 0; j < m; j++) { + // avoid double count (x, y) + if (j !== y) cnt += (picture[x][j] === 'B' ? 1 : 0); + } + return picture[x][y] === 'B' && cnt === 1; + }; + + let n = picture.length; + let m = picture[0].length; + + let answer = 0; + for (let j = 0; j < m; j++) { + answer += check(0, j) ? 1 : 0; + } + for (let i = 1; i < n; i++) { + answer += check(i, 0) ? 1 : 0; + } + // Convert cell 'B' to '1' and 'W' to '0' + for (let j = 0; j < m; j++) { + picture[0][j] = (picture[0][j] === 'B' ? '1' : '0'); + } + + for (let i = 0; i < n; i++) { + picture[i][0] = (picture[i][0] === 'B' ? '1' : '0'); + } + + // If the cell is black increment the count of corresponding row and column by 1 + for (let i = 1; i < n; i++) { + for (let j = 1; j < m; j++) { + if (picture[i][j] === 'B') { + picture[i][0] = String.fromCharCode(picture[i][0].charCodeAt(0) + 1); + picture[0][j] = String.fromCharCode(picture[0][j].charCodeAt(0) + 1); + } + } + } + + for (let i = 1; i < n; i++) { + for (let j = 1; j < m; j++) { + if (picture[i][j] === 'B') { + if (picture[0][j] === '1' && picture[i][0] === '1') { + answer++; + } + } + } + } + + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(M \cdot N)$ +- Space complexity: $O(1)$ constant space + +> Where $M$ is the number of rows in the given matrix `picture`, and $N$ is the number of columns in it. From ea22fdfd525ca7b4bb699a0a8b72e1e255ab057a Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 19 Nov 2025 15:02:09 -0800 Subject: [PATCH 30/32] add sparse-matrix-multiplication.md article --- articles/sparse-matrix-multiplication.md | 646 +++++++++++++++++++++++ 1 file changed, 646 insertions(+) create mode 100644 articles/sparse-matrix-multiplication.md diff --git a/articles/sparse-matrix-multiplication.md b/articles/sparse-matrix-multiplication.md new file mode 100644 index 000000000..ac99369dc --- /dev/null +++ b/articles/sparse-matrix-multiplication.md @@ -0,0 +1,646 @@ +## 1. Naive Iteration + +::tabs-start + +```python +class Solution: + def multiply(self, mat1: List[List[int]], mat2: List[List[int]]) -> List[List[int]]: + + # Product matrix. + ans = [[0] * len(mat2[0]) for _ in range(len(mat1))] + + for row_index, row_elements in enumerate(mat1): + for element_index, row_element in enumerate(row_elements): + # If current element of mat1 is non-zero then iterate over all columns of mat2. + if row_element: + for col_index, col_element in enumerate(mat2[element_index]): + ans[row_index][col_index] += row_element * col_element + + return ans +``` + +```java +class Solution { + public int[][] multiply(int[][] mat1, int[][] mat2) { + int n = mat1.length; + int k = mat1[0].length; + int m = mat2[0].length; + + // Product matrix will have 'n x m' size. + int[][] ans = new int[n][m]; + + for (int rowIndex = 0; rowIndex < n; ++rowIndex) { + for (int elementIndex = 0; elementIndex < k; ++elementIndex) { + // If current element of mat1 is non-zero then iterate over all columns of mat2. + if (mat1[rowIndex][elementIndex] != 0) { + for (int colIndex = 0; colIndex < m; ++colIndex) { + ans[rowIndex][colIndex] += mat1[rowIndex][elementIndex] * mat2[elementIndex][colIndex]; + } + } + } + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector> multiply(vector>& mat1, vector>& mat2) { + int n = mat1.size(); + int k = mat1[0].size(); + int m = mat2[0].size(); + + // Product matrix will have 'n x m' size. + vector> ans (n, vector(m, 0)); + + for (int rowIndex = 0; rowIndex < n; ++rowIndex) { + for (int elementIndex = 0; elementIndex < k; ++elementIndex) { + // If current element of mat1 is non-zero then iterate over all columns of mat2. + if (mat1[rowIndex][elementIndex] != 0) { + for (int colIndex = 0; colIndex < m; ++colIndex) { + ans[rowIndex][colIndex] += mat1[rowIndex][elementIndex] * mat2[elementIndex][colIndex]; + } + } + } + } + + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat1 + * @param {number[][]} mat2 + * @return {number[][]} + */ + multiply(mat1, mat2) { + // Product matrix. + let ans = Array(mat1.length).fill(0).map(x => Array(mat2[0].length).fill(0)) + + mat1.forEach((rowElements, rowIndex) => { + rowElements.forEach((rowElement, elementIndex) => { + // If current element of mat1 is non-zero then iterate over all columns of mat2. + if (rowElement) { + mat2[elementIndex].forEach((colElement, colIndex) => { + ans[rowIndex][colIndex] += rowElement * colElement; + }); + } + }); + }); + + return ans + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m \cdot k \cdot n)$ +- Space complexity: $O(1)$ + +> Where $m$ and $k$ represent the number of rows and columns in `mat1`, respectively and $k$ and $n$ represent the number of rows and columns in `mat2`, respectively. + +--- + +## 2. List of Lists + +::tabs-start + +```python +class Solution: + def multiply(self, mat1: List[List[int]], mat2: List[List[int]]) -> List[List[int]]: + def compress_matrix(matrix: List[List[int]]) -> List[List[int]]: + rows, cols = len(matrix), len(matrix[0]) + compressed_matrix = [[] for _ in range(rows)] + for row in range(rows): + for col in range(cols): + if matrix[row][col]: + compressed_matrix[row].append([matrix[row][col], col]) + return compressed_matrix + + m = len(mat1) + k = len(mat1[0]) + n = len(mat2[0]) + + # Store the non-zero values of each matrix. + A = compress_matrix(mat1) + B = compress_matrix(mat2) + + ans = [[0] * n for _ in range(m)] + + for mat1_row in range(m): + # Iterate on all current 'row' non-zero elements of mat1. + for element1, mat1_col in A[mat1_row]: + # Multiply and add all non-zero elements of mat2 + # where the row is equal to col of current element of mat1. + for element2, mat2_col in B[mat1_col]: + ans[mat1_row][mat2_col] += element1 * element2 + + return ans +``` + +```java +class Solution { + public ArrayList>> compressMatrix(int[][] matrix) { + int rows = matrix.length; + int cols = matrix[0].length; + + ArrayList>> compressedMatrix = new ArrayList<>(); + + for (int row = 0; row < rows; ++row) { + ArrayList> currRow = new ArrayList<>(); + for (int col = 0; col < cols; ++col) { + if (matrix[row][col] != 0) { + currRow.add(new Pair(matrix[row][col], col)); + } + } + compressedMatrix.add(currRow); + } + return compressedMatrix; + } + + public int[][] multiply(int[][] mat1, int[][] mat2) { + int m = mat1.length; + int k = mat1[0].length; + int n = mat2[0].length; + + // Store the non-zero values of each matrix. + ArrayList>> A = compressMatrix(mat1); + ArrayList>> B = compressMatrix(mat2); + + int[][] ans = new int[m][n]; + + for (int mat1Row = 0; mat1Row < m; ++mat1Row) { + // Iterate on all current 'row' non-zero elements of mat1. + for (Pair mat1Element : A.get(mat1Row)) { + int element1 = (int)mat1Element.getKey(); + int mat1Col = (int)mat1Element.getValue(); + + // Multiply and add all non-zero elements of mat2 + // where the row is equal to col of current element of mat1. + for (Pair mat2Element : B.get(mat1Col)) { + int element2 = (int)mat2Element.getKey(); + int mat2Col = (int)mat2Element.getValue(); + ans[mat1Row][mat2Col] += element1 * element2; + } + } + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector>> compressMatrix(vector>& matrix) { + int rows = matrix.size(); + int cols = matrix[0].size(); + vector>> compressedMatrix(rows); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + if (matrix[row][col] != 0) { + compressedMatrix[row].push_back({matrix[row][col], col}); + } + } + } + return compressedMatrix; + } + + vector> multiply(vector>& mat1, vector>& mat2) { + int m = mat1.size(); + int k = mat1[0].size(); + int n = mat2[0].size(); + + // Store the non-zero values of each matrix. + vector>> A = compressMatrix(mat1); + vector>> B = compressMatrix(mat2); + + vector> ans(m, vector(n, 0)); + + for (int mat1Row = 0; mat1Row < m; ++mat1Row) { + // Iterate on all current 'row' non-zero elements of mat1. + for (auto [element1, mat1Col] : A[mat1Row]) { + + // Multiply and add all non-zero elements of mat2 + // where the row is equal to col of current element of mat1. + for (auto [element2, mat2Col] : B[mat1Col]) { + ans[mat1Row][mat2Col] += element1 * element2; + } + } + } + + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat1 + * @param {number[][]} mat2 + * @return {number[][]} + */ + multiply(mat1, mat2) { + let compressMatrix = (matrix) => { + let rows = matrix.length; + let cols = matrix[0].length; + let compressedMatrix = []; + matrix.forEach((rowElements, row) => { + let currRow = []; + rowElements.forEach((element, col) => { + if (element) { + currRow.push([element, col]); + } + }); + compressedMatrix.push(currRow); + }); + return compressedMatrix; + } + + let m = mat1.length; + let k = mat1[0].length; + let n = mat2[0].length; + + // Store the non-zero values of each matrix. + let A = compressMatrix(mat1); + let B = compressMatrix(mat2); + + let ans = Array(m).fill(0).map(x => Array(n).fill(0)); + + for (let mat1Row = 0; mat1Row < m; ++mat1Row) { + // Iterate on all current 'row' non-zero elements of mat1. + for (let mat1Element of A[mat1Row]) { + let [element1, mat1Col] = mat1Element; + + // Multiply and add all non-zero elements of mat2 + // where the row is equal to col of current element of mat1. + for (let mat2Element of B[mat1Col]) { + let [element2, mat2Col] = mat2Element; + ans[mat1Row][mat2Col] += element1 * element2; + } + } + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m \cdot k \cdot n)$ +- Space complexity: $O(m \cdot k + k \cdot n)$ + +> Where $m$ and $k$ represent the number of rows and columns in `mat1`, respectively and $k$ and $n$ represent the number of rows and columns in `mat2`, respectively. + +--- + +## 3. Yale Format + +::tabs-start + +```python +class SparseMatrix: + def __init__(self, matrix: List[List[int]], col_wise: bool): + self.values, self.row_index, self.col_index = self.compress_matrix(matrix, col_wise) + + def compress_matrix(self, matrix: List[List[int]], col_wise: bool): + return self.compress_col_wise(matrix) if col_wise else self.compress_row_wise(matrix) + + # Compressed Sparse Row + def compress_row_wise(self, matrix: List[List[int]]): + values = [] + row_index = [0] + col_index = [] + + for row in range(len(matrix)): + for col in range(len(matrix[0])): + if matrix[row][col]: + values.append(matrix[row][col]) + col_index.append(col) + row_index.append(len(values)) + + return values, row_index, col_index + + # Compressed Sparse Column + def compress_col_wise(self, matrix: List[List[int]]): + values = [] + row_index = [] + col_index = [0] + + for col in range(len(matrix[0])): + for row in range(len(matrix)): + if matrix[row][col]: + values.append(matrix[row][col]) + row_index.append(row) + col_index.append(len(values)) + + return values, row_index, col_index + +class Solution: + def multiply(self, mat1: List[List[int]], mat2: List[List[int]]) -> List[List[int]]: + A = SparseMatrix(mat1, False) + B = SparseMatrix(mat2, True) + + ans = [[0] * len(mat2[0]) for _ in range(len(mat1))] + + for row in range(len(ans)): + for col in range(len(ans[0])): + + # Row element range indices + mat1_row_start = A.row_index[row] + mat1_row_end = A.row_index[row + 1] + + # Column element range indices + mat2_col_start = B.col_index[col] + mat2_col_end = B.col_index[col + 1] + + # Iterate over both row and column. + while mat1_row_start < mat1_row_end and mat2_col_start < mat2_col_end: + if A.col_index[mat1_row_start] < B.row_index[mat2_col_start]: + mat1_row_start += 1 + elif A.col_index[mat1_row_start] > B.row_index[mat2_col_start]: + mat2_col_start += 1 + # Row index and col index are same so we can multiply these elements. + else: + ans[row][col] += A.values[mat1_row_start] * B.values[mat2_col_start] + mat1_row_start += 1 + mat2_col_start += 1 + + return ans +``` + +```java +class Solution { + class SparseMatrix { + public int cols = 0, rows = 0; + public ArrayList values = new ArrayList<>(); + public ArrayList colIndex = new ArrayList<>(); + public ArrayList rowIndex = new ArrayList<>(); + + // Compressed Sparse Row + public SparseMatrix(int[][] matrix) { + rows = matrix.length; + cols = matrix[0].length; + rowIndex.add(0); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + if (matrix[row][col] != 0) { + values.add(matrix[row][col]); + colIndex.add(col); + } + } + rowIndex.add(values.size()); + } + } + + // Compressed Sparse Column + public SparseMatrix(int[][] matrix, boolean colWise) { + rows = matrix.length; + cols = matrix[0].length; + colIndex.add(0); + + for (int col = 0; col < cols; ++col) { + for (int row = 0; row < rows; ++row) { + if (matrix[row][col] != 0) { + values.add(matrix[row][col]); + rowIndex.add(row); + } + } + colIndex.add(values.size()); + } + } + }; + + + public int[][] multiply(int[][] mat1, int[][] mat2) { + SparseMatrix A = new SparseMatrix(mat1); + SparseMatrix B = new SparseMatrix(mat2, true); + + int[][] ans = new int[mat1.length][mat2[0].length]; + + for (int row = 0; row < ans.length; ++row) { + for (int col = 0; col < ans[0].length; ++col) { + + // Row element range indices + int matrixOneRowStart = A.rowIndex.get(row); + int matrixOneRowEnd = A.rowIndex.get(row + 1); + + // Column element range indices + int matrixTwoColStart = B.colIndex.get(col); + int matrixTwoColEnd = B.colIndex.get(col + 1); + + // Iterate over both row and column. + while (matrixOneRowStart < matrixOneRowEnd && matrixTwoColStart < matrixTwoColEnd) { + if (A.colIndex.get(matrixOneRowStart) < B.rowIndex.get(matrixTwoColStart)) { + matrixOneRowStart++; + } else if (A.colIndex.get(matrixOneRowStart) > B.rowIndex.get(matrixTwoColStart)) { + matrixTwoColStart++; + } else { + // Row index and col index are same so we can multiply these elements. + ans[row][col] += A.values.get(matrixOneRowStart) * B.values.get(matrixTwoColStart); + matrixOneRowStart++; + matrixTwoColStart++; + } + } + } + } + + return ans; + } +} +``` + +```cpp +class SparseMatrix { +public: + int cols = 0, rows = 0; + vector values, colIndex, rowIndex; + + // Compressed Sparse Row + SparseMatrix(vector>& matrix) { + rows = matrix.size(); + cols = matrix[0].size(); + rowIndex.push_back(0); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + if (matrix[row][col]) { + values.push_back(matrix[row][col]); + colIndex.push_back(col); + } + } + rowIndex.push_back(values.size()); + } + } + + // Compressed Sparse Column + SparseMatrix(vector>& matrix, bool colWise) { + rows = matrix.size(); + cols = matrix[0].size(); + colIndex.push_back(0); + + for (int col = 0; col < cols; ++col) { + for (int row = 0; row < rows; ++row) { + if (matrix[row][col]) { + values.push_back(matrix[row][col]); + rowIndex.push_back(row); + } + } + colIndex.push_back(values.size()); + } + } +}; + +class Solution { +public: + vector> multiply(vector>& mat1, vector>& mat2) { + SparseMatrix A (mat1); + SparseMatrix B (mat2, true); + + vector> ans(mat1.size(), vector(mat2[0].size(), 0)); + + for (int row = 0; row < ans.size(); ++row) { + for (int col = 0; col < ans[0].size(); ++col) { + + // Row element range indices + int matrixOneRowStart = A.rowIndex[row]; + int matrixOneRowEnd = A.rowIndex[row + 1]; + + // Column element range indices + int matrixTwoColStart = B.colIndex[col]; + int matrixTwoColEnd = B.colIndex[col + 1]; + + // Iterate over both row and column. + while (matrixOneRowStart < matrixOneRowEnd && matrixTwoColStart < matrixTwoColEnd) { + if (A.colIndex[matrixOneRowStart] < B.rowIndex[matrixTwoColStart]) { + matrixOneRowStart++; + } else if (A.colIndex[matrixOneRowStart] > B.rowIndex[matrixTwoColStart]) { + matrixTwoColStart++; + } else { + // Row index and col index are same so we can multiply these elements. + ans[row][col] += A.values[matrixOneRowStart] * B.values[matrixTwoColStart]; + matrixOneRowStart++; + matrixTwoColStart++; + } + } + } + } + + return ans; + } +}; +``` + +```javascript +class SparseMatrix { + constructor(matrix, colWise) { + [this.values, this.rowIndex, this.colIndex] = this.compressMatrix(matrix, colWise); + } + + compressMatrix(matrix, colWise) { + return (colWise ? this.compressColWise(matrix) : this.compressRowWise(matrix)); + } + + // Compressed Sparse Row + compressRowWise(matrix) { + let values = [] + let rowIndex = [0] + let colIndex = [] + + for (let row = 0; row < matrix.length; ++row) { + for (let col = 0; col < matrix[0].length; ++col) { + if (matrix[row][col]) { + values.push(matrix[row][col]); + colIndex.push(col); + } + } + rowIndex.push(values.length); + } + + return [values, rowIndex, colIndex]; + } + + // Compressed Sparse Col + compressColWise(matrix) { + let values = [] + let rowIndex = [] + let colIndex = [0] + + for (let col = 0; col < matrix[0].length; ++col) { + for (let row = 0; row < matrix.length; ++row) { + if (matrix[row][col]) { + values.push(matrix[row][col]); + rowIndex.push(row); + } + } + colIndex.push(values.length); + } + + return [values, rowIndex, colIndex]; + } +} + +class Solution { + /** + * @param {number[][]} mat1 + * @param {number[][]} mat2 + * @return {number[][]} + */ + multiply(mat1, mat2) { + let A = new SparseMatrix(mat1, false) + let B = new SparseMatrix(mat2, true) + + let ans = Array(mat1.length).fill(0).map(x => Array(mat2[0].length).fill(0)); + + ans.forEach((_, row) => { + ans[row].forEach((_, col) => { + // Row element range indices + let matrixOneRowStart = A.rowIndex[row]; + let matrixOneRowEnd = A.rowIndex[row + 1]; + // Column element range indices + let matrixTwoColStart = B.colIndex[col]; + let matrixTwoColEnd = B.colIndex[col + 1]; + // Iterate over both row and column. + while (matrixOneRowStart < matrixOneRowEnd && matrixTwoColStart < matrixTwoColEnd) { + if (A.colIndex[matrixOneRowStart] < B.rowIndex[matrixTwoColStart]) { + matrixOneRowStart++; + } else if (A.colIndex[matrixOneRowStart] > B.rowIndex[matrixTwoColStart]) { + matrixTwoColStart++; + } else { + // Row index and col index are same so we can multiply these elements. + ans[row][col] += A.values[matrixOneRowStart] * B.values[matrixTwoColStart]; + matrixOneRowStart++; + matrixTwoColStart++; + } + } + }); + }); + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m \cdot n \cdot k)$ +- Space complexity: $O(m \cdot k + k \cdot n)$ + +> Where $m$ and $k$ represent the number of rows and columns in `mat1`, respectively and $k$ and $n$ represent the number of rows and columns in `mat2`, respectively. From 19ccb9498ea5d71f22529048385afabcbc2f3504 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 19 Nov 2025 17:31:14 -0800 Subject: [PATCH 31/32] add candy-crush.md article --- articles/candy-crush.md | 650 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 650 insertions(+) create mode 100644 articles/candy-crush.md diff --git a/articles/candy-crush.md b/articles/candy-crush.md new file mode 100644 index 000000000..6258d3a81 --- /dev/null +++ b/articles/candy-crush.md @@ -0,0 +1,650 @@ +## 1. Separate Steps: Find, Crush, Drop + +::tabs-start + +```python +class Solution: + def candyCrush(self, board: List[List[int]]) -> List[List[int]]: + m, n = len(board), len(board[0]) + + def find(): + crushed_set = set() + + # Check vertically adjacent candies + for r in range(1, m - 1): + for c in range(n): + if board[r][c] == 0: + continue + if board[r][c] == board[r - 1][c] == board[r + 1][c]: + crushed_set.add((r, c)) + crushed_set.add((r - 1, c)) + crushed_set.add((r + 1, c)) + + # Check horizontally adjacent candies + for r in range(m): + for c in range(1, n - 1): + if board[r][c] == 0: + continue + if board[r][c] == board[r][c - 1] == board[r][c + 1]: + crushed_set.add((r, c)) + crushed_set.add((r, c - 1)) + crushed_set.add((r, c + 1)) + return crushed_set + + # Set the value of each candies to be crushed as 0 + def crush(crushed_set): + for (r, c) in crushed_set: + board[r][c] = 0 + + def drop(): + for c in range(n): + lowest_zero = -1 + + # Iterate over each column + for r in range(m - 1, -1, -1): + if board[r][c] == 0: + lowest_zero = max(lowest_zero, r) + + # Swap current non-zero candy with the lowest zero. + elif lowest_zero >= 0: + board[r][c], board[lowest_zero][c] = board[lowest_zero][c], board[r][c] + lowest_zero -= 1 + + # Continue with the three steps until we can no longer find any crushable candies. + crushed_set = find() + while crushed_set: + crush(crushed_set) + drop() + crushed_set = find() + + return board +``` + +```java +class Solution { + int m, n; + + Set> find(int[][] board) { + Set> crushedSet = new HashSet<>(); + + // Check vertically adjacent candies + for (int r = 1; r < m - 1; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] == 0) { + continue; + } + if (board[r][c] == board[r - 1][c] && board[r][c] == board[r + 1][c]) { + crushedSet.add(new Pair<>(r, c)); + crushedSet.add(new Pair<>(r - 1, c)); + crushedSet.add(new Pair<>(r + 1, c)); + } + } + } + + // Check horizontally adjacent candies + for (int r = 0; r < m; r++) { + for (int c = 1; c < n - 1; c++) { + if (board[r][c] == 0) { + continue; + } + if (board[r][c] == board[r][c - 1] && board[r][c] == board[r][c + 1]) { + crushedSet.add(new Pair<>(r, c)); + crushedSet.add(new Pair<>(r, c - 1)); + crushedSet.add(new Pair<>(r, c + 1)); + } + } + } + return crushedSet; + } + + void crush(int[][] board, Set> crushedSet) { + for (Pair pair : crushedSet) { + int r = pair.getKey(); + int c = pair.getValue(); + board[r][c] = 0; + } + } + + void drop(int[][] board) { + for (int c = 0; c < n; c++) { + int lowestZero = -1; + + // Iterate over each column + for (int r = m - 1; r >= 0; r--) { + if (board[r][c] == 0) { + lowestZero = Math.max(lowestZero, r); + } else if (lowestZero >= 0) { + int temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } + + public int[][] candyCrush(int[][] board) { + m = board.length; + n = board[0].length; + Set> crushedSet = find(board); + while (!crushedSet.isEmpty()) { + crush(board, crushedSet); + drop(board); + crushedSet = find(board); + } + + return board; + } +} +``` + +```cpp +class Solution { + int m, n; + + set> find(vector>& board) { + set> crushedSet; + + // Check vertically adjacent candies + for (int r = 1; r < m - 1; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] == 0) { + continue; + } + if (board[r][c] == board[r - 1][c] && board[r][c] == board[r + 1][c]) { + crushedSet.insert({r, c}); + crushedSet.insert({r - 1, c}); + crushedSet.insert({r + 1, c}); + } + } + } + + // Check horizontally adjacent candies + for (int r = 0; r < m; r++) { + for (int c = 1; c < n - 1; c++) { + if (board[r][c] == 0) { + continue; + } + if (board[r][c] == board[r][c - 1] && board[r][c] == board[r][c + 1]) { + crushedSet.insert({r, c}); + crushedSet.insert({r, c - 1}); + crushedSet.insert({r, c + 1}); + } + } + } + + return crushedSet; + } + + void crush(vector>& board, set>& crushedSet) { + for (const auto& p : crushedSet) { + int r = p.first; + int c = p.second; + board[r][c] = 0; + } + } + + void drop(vector>& board) { + for (int c = 0; c < n; c++) { + int lowestZero = -1; + + // Iterate over each column + for (int r = m - 1; r >= 0; r--) { + if (board[r][c] == 0) { + lowestZero = max(lowestZero, r); + } else if (lowestZero >= 0) { + int temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } + +public: + vector> candyCrush(vector>& board) { + m = board.size(); + n = board[0].size(); + + set> crushedSet = find(board); + while (!crushedSet.empty()) { + crush(board, crushedSet); + drop(board); + crushedSet = find(board); + } + + return board; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number[][]} + */ + candyCrush(board) { + this.m = board.length; + this.n = board[0].length; + + let crushedSet = this.find(board); + while (crushedSet.size > 0) { + this.crush(board, crushedSet); + this.drop(board); + crushedSet = this.find(board); + } + + return board; + } + + /** + * @param {number[][]} board + * @return {Set} + */ + find(board) { + let crushedSet = new Set(); + + // Check vertically adjacent candies + for (let r = 1; r < this.m - 1; r++) { + for (let c = 0; c < this.n; c++) { + if (board[r][c] === 0) { + continue; + } + if (board[r][c] === board[r - 1][c] && board[r][c] === board[r + 1][c]) { + crushedSet.add(`${r},${c}`); + crushedSet.add(`${r - 1},${c}`); + crushedSet.add(`${r + 1},${c}`); + } + } + } + + // Check horizontally adjacent candies + for (let r = 0; r < this.m; r++) { + for (let c = 1; c < this.n - 1; c++) { + if (board[r][c] === 0) { + continue; + } + if (board[r][c] === board[r][c - 1] && board[r][c] === board[r][c + 1]) { + crushedSet.add(`${r},${c}`); + crushedSet.add(`${r},${c - 1}`); + crushedSet.add(`${r},${c + 1}`); + } + } + } + + return crushedSet; + } + + /** + * @param {number[][]} board + * @param {Set} crushedSet + * @return {void} + */ + crush(board, crushedSet) { + for (let key of crushedSet) { + let [r, c] = key.split(',').map(Number); + board[r][c] = 0; + } + } + + /** + * @param {number[][]} board + * @return {void} + */ + drop(board) { + for (let c = 0; c < this.n; c++) { + let lowestZero = -1; + + // Iterate over each column + for (let r = this.m - 1; r >= 0; r--) { + if (board[r][c] === 0) { + lowestZero = Math.max(lowestZero, r); + } else if (lowestZero >= 0) { + let temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m^2 \cdot n^2)$ +- Space complexity: $O(m \cdot n)$ + +> Where $m × n$ is the size of the grid `board` + +--- + +## 2. In-place Modification + +::tabs-start + +```python +class Solution: + def candyCrush(self, board: List[List[int]]) -> List[List[int]]: + m, n = len(board), len(board[0]) + + def find_and_crush(): + complete = True + + # Check vertically adjacent candies + for r in range(1, m - 1): + for c in range(n): + if board[r][c] == 0: + continue + if abs(board[r][c]) == abs(board[r - 1][c]) == abs(board[r + 1][c]): + board[r][c] = -abs(board[r][c]) + board[r - 1][c] = -abs(board[r - 1][c]) + board[r + 1][c] = -abs(board[r + 1][c]) + complete = False + + # Check horizontally adjacent candies + for r in range(m): + for c in range(1, n - 1): + if board[r][c] == 0: + continue + if abs(board[r][c]) == abs(board[r][c - 1]) == abs(board[r][c + 1]): + board[r][c] = -abs(board[r][c]) + board[r][c - 1] = -abs(board[r][c - 1]) + board[r][c + 1] = -abs(board[r][c + 1]) + complete = False + + # Set the value of each candies to be crushed as 0 + for r in range(m): + for c in range(n): + if board[r][c] < 0: + board[r][c] = 0 + return complete + + def drop(): + for c in range(n): + lowest_zero = -1 + + # Iterate over each column + for r in range(m - 1, -1, -1): + if board[r][c] == 0: + lowest_zero = max(lowest_zero, r) + + # Swap current non-zero candy with the lowest zero. + elif lowest_zero >= 0: + board[r][c], board[lowest_zero][c] = board[lowest_zero][c], board[r][c] + lowest_zero -= 1 + + # Continue with the three steps until we can no longer find any crushable candies. + while not find_and_crush(): + drop() + + return board +``` + +```java +class Solution { + int m, n; + boolean findAndCrush(int[][] board) { + boolean complete = true; + + // Check vertically adjacent candies + for (int r = 1; r < m - 1; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] == 0) { + continue; + } + if (Math.abs(board[r][c]) == Math.abs(board[r - 1][c]) && Math.abs(board[r][c]) == Math.abs(board[r + 1][c])) { + board[r][c] = -Math.abs(board[r][c]); + board[r - 1][c] = -Math.abs(board[r - 1][c]); + board[r + 1][c] = -Math.abs(board[r + 1][c]); + complete = false; + } + } + } + + // Check horizontally adjacent candies + for (int r = 0; r < m; r++) { + for (int c = 1; c < n - 1; c++) { + if (board[r][c] == 0) { + continue; + } + if (Math.abs(board[r][c]) == Math.abs(board[r][c - 1]) && Math.abs(board[r][c]) == Math.abs(board[r][c + 1])) { + board[r][c] = -Math.abs(board[r][c]); + board[r][c - 1] = -Math.abs(board[r][c - 1]); + board[r][c + 1] = -Math.abs(board[r][c + 1]); + complete = false; + } + } + } + + // Set the value of each candy to be crushed as 0 + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] < 0) { + board[r][c] = 0; + } + } + } + + return complete; + } + + void drop(int[][] board) { + for (int c = 0; c < n; c++) { + int lowestZero = -1; + + // Iterate over each column + for (int r = m - 1; r >= 0; r--) { + if (board[r][c] == 0) { + lowestZero = Math.max(lowestZero, r); + } else if (lowestZero >= 0) { + int temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } + + public int[][] candyCrush(int[][] board) { + m = board.length; + n = board[0].length; + + // Continue with the three steps until we can no longer find any crushable candies. + while (!findAndCrush(board)) { + drop(board); + } + + return board; + } +} +``` + +```cpp +class Solution { + int m, n; + + bool findAndCrush(vector>& board) { + bool complete = true; + + // Check vertically adjacent candies + for (int r = 1; r < m - 1; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] == 0) { + continue; + } + if (abs(board[r][c]) == abs(board[r - 1][c]) && abs(board[r][c]) == abs(board[r + 1][c])) { + board[r][c] = -abs(board[r][c]); + board[r - 1][c] = -abs(board[r - 1][c]); + board[r + 1][c] = -abs(board[r + 1][c]); + complete = false; + } + } + } + + // Check horizontally adjacent candies + for (int r = 0; r < m; r++) { + for (int c = 1; c < n - 1; c++) { + if (board[r][c] == 0) { + continue; + } + if (abs(board[r][c]) == abs(board[r][c - 1]) && abs(board[r][c]) == abs(board[r][c + 1])) { + board[r][c] = -abs(board[r][c]); + board[r][c - 1] = -abs(board[r][c - 1]); + board[r][c + 1] = -abs(board[r][c + 1]); + complete = false; + } + } + } + + // Set the value of each candy to be crushed as 0 + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (board[r][c] < 0) { + board[r][c] = 0; + } + } + } + + return complete; + } + + void drop(vector>& board) { + for (int c = 0; c < n; c++) { + int lowestZero = -1; + + // Iterate over each column + for (int r = m - 1; r >= 0; r--) { + if (board[r][c] == 0) { + lowestZero = max(lowestZero, r); + } else if (lowestZero >= 0) { + int temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } + +public: + vector> candyCrush(vector>& board) { + m = board.size(); + n = board[0].size(); + + // Continue with the three steps until we can no longer find any crushable candies. + while (!findAndCrush(board)) { + drop(board); + } + + return board; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number[][]} + */ + candyCrush(board) { + this.m = board.length; + this.n = board[0].length; + + // Continue with the three steps until we can no longer find any crushable candies. + while (!this.findAndCrush(board)) { + this.drop(board); + } + + return board; + } + + /** + * @param {number[][]} board + * @return {boolean} + */ + findAndCrush(board) { + let complete = true; + + // Check vertically adjacent candies + for (let r = 1; r < this.m - 1; r++) { + for (let c = 0; c < this.n; c++) { + if (board[r][c] === 0) { + continue; + } + if (Math.abs(board[r][c]) === Math.abs(board[r - 1][c]) && Math.abs(board[r][c]) === Math.abs(board[r + 1][c])) { + board[r][c] = -Math.abs(board[r][c]); + board[r - 1][c] = -Math.abs(board[r - 1][c]); + board[r + 1][c] = -Math.abs(board[r + 1][c]); + complete = false; + } + } + } + + // Check horizontally adjacent candies + for (let r = 0; r < this.m; r++) { + for (let c = 1; c < this.n - 1; c++) { + if (board[r][c] === 0) { + continue; + } + if (Math.abs(board[r][c]) === Math.abs(board[r][c - 1]) && Math.abs(board[r][c]) === Math.abs(board[r][c + 1])) { + board[r][c] = -Math.abs(board[r][c]); + board[r][c - 1] = -Math.abs(board[r][c - 1]); + board[r][c + 1] = -Math.abs(board[r][c + 1]); + complete = false; + } + } + } + + // Set the value of each candy to be crushed as 0 + for (let r = 0; r < this.m; r++) { + for (let c = 0; c < this.n; c++) { + if (board[r][c] < 0) { + board[r][c] = 0; + } + } + } + + return complete; + } + + /** + * @param {number[][]} board + * @return {void} + */ + drop(board) { + for (let c = 0; c < this.n; c++) { + let lowestZero = -1; + + // Iterate over each column + for (let r = this.m - 1; r >= 0; r--) { + if (board[r][c] === 0) { + lowestZero = Math.max(lowestZero, r); + } else if (lowestZero >= 0) { + let temp = board[r][c]; + board[r][c] = board[lowestZero][c]; + board[lowestZero][c] = temp; + lowestZero--; + } + } + } + } +} + +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m^2 \cdot n^2)$ +- Space complexity: $O(1)$ constant space + +> Where $m × n$ is the size of the grid `board` From 4273f6724939be40f1bb46b18ead405e5ae94e6f Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 20 Nov 2025 14:00:42 -0800 Subject: [PATCH 32/32] add missing-ranges.md article --- articles/missing-ranges.md | 152 +++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 articles/missing-ranges.md diff --git a/articles/missing-ranges.md b/articles/missing-ranges.md new file mode 100644 index 000000000..e9ca33bb1 --- /dev/null +++ b/articles/missing-ranges.md @@ -0,0 +1,152 @@ +## 1. Linear Scan + +::tabs-start + +```python +class Solution: + def findMissingRanges( + self, nums: List[int], lower: int, upper: int + ) -> List[List[int]]: + n = len(nums) + missing_ranges = [] + if n == 0: + missing_ranges.append([lower, upper]) + return missing_ranges + + # Check for any missing numbers between the lower bound and nums[0]. + if lower < nums[0]: + missing_ranges.append([lower, nums[0] - 1]) + + # Check for any missing numbers between successive elements of nums. + for i in range(n - 1): + if nums[i + 1] - nums[i] <= 1: + continue + missing_ranges.append([nums[i] + 1, nums[i + 1] - 1]) + + # Check for any missing numbers between the last element of nums and the upper bound. + if upper > nums[n - 1]: + missing_ranges.append([nums[n - 1] + 1, upper]) + + return missing_ranges +``` + +```java +class Solution { + public List> findMissingRanges( + int[] nums, + int lower, + int upper + ) { + int n = nums.length; + List> missingRanges = new ArrayList<>(); + + if (n == 0) { + missingRanges.add(Arrays.asList(lower, upper)); + return missingRanges; + } + // Check for any missing numbers between the lower bound and nums[0]. + if (lower < nums[0]) { + missingRanges.add(Arrays.asList(lower, nums[0] - 1)); + } + + // Check for any missing numbers between successive elements of nums. + for (int i = 0; i < n - 1; i++) { + if (nums[i + 1] - nums[i] <= 1) { + continue; + } + missingRanges.add(Arrays.asList(nums[i] + 1, nums[i + 1] - 1)); + } + + // Check for any missing numbers between the last element of nums and the upper bound. + if (upper > nums[n - 1]) { + missingRanges.add(Arrays.asList(nums[n - 1] + 1, upper)); + } + + return missingRanges; + } +} +``` + +```cpp +class Solution { +public: + vector> findMissingRanges(vector& nums, int lower, + int upper) { + int n = nums.size(); + vector> missingRanges; + if (n == 0) { + missingRanges.push_back(vector{lower, upper}); + return missingRanges; + } + + // Check for any missing numbers between the lower bound and nums[0]. + if (lower < nums[0]) { + missingRanges.push_back(vector{lower, nums[0] - 1}); + } + + // Check for any missing numbers between successive elements of nums. + for (int i = 0; i < n - 1; i++) { + if (nums[i + 1] - nums[i] <= 1) { + continue; + } + missingRanges.push_back(vector{nums[i] + 1, nums[i + 1] - 1}); + } + + // Check for any missing numbers between the last element of nums and + // the upper bound. + if (upper > nums[n - 1]) { + missingRanges.push_back(vector{nums[n - 1] + 1, upper}); + } + + return missingRanges; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} lower + * @param {number} upper + * @return {number[][]} + */ + findMissingRanges(nums, lower, upper) { + let n = nums.length; + let missingRanges = []; + if (n === 0) { + missingRanges.push([lower, upper]); + return missingRanges; + } + + // Check for any missing numbers between the lower bound and nums[0]. + if (lower < nums[0]) { + missingRanges.push([lower, nums[0] - 1]); + } + + // Check for any missing numbers between successive elements of nums. + for (let i = 0; i < n - 1; i++) { + if (nums[i + 1] - nums[i] <= 1) { + continue; + } + missingRanges.push([nums[i] + 1, nums[i + 1] - 1]); + } + + // Check for any missing numbers between the last element of nums and the upper bound. + if (upper > nums[n - 1]) { + missingRanges.push([nums[n - 1] + 1, upper]); + } + + return missingRanges; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ constant space + +> Where $n$ is the number of elements in `nums`.