From b514abf957c536dc492e2a00a97ba551d142df70 Mon Sep 17 00:00:00 2001 From: Sri Hari Date: Sat, 28 Dec 2024 23:27:15 +0530 Subject: [PATCH 1/2] Batch-4/Neetcode-All/Added-articles --- articles/arithmetic-slices-ii-subsequence.md | 499 ++++++++++ articles/cherry-pickup-ii.md | 559 ++++++++++++ articles/design-word-search-data-structure.md | 2 +- articles/freedom-trail.md | 142 +++ articles/k-inverse-pairs-array.md | 599 ++++++++++++ .../minimum-difficulty-of-a-job-schedule.md | 682 ++++++++++++++ articles/minimum-falling-path-sum-ii.md | 854 ++++++++++++++++++ 7 files changed, 3336 insertions(+), 1 deletion(-) create mode 100644 articles/arithmetic-slices-ii-subsequence.md create mode 100644 articles/cherry-pickup-ii.md create mode 100644 articles/freedom-trail.md create mode 100644 articles/k-inverse-pairs-array.md create mode 100644 articles/minimum-difficulty-of-a-job-schedule.md create mode 100644 articles/minimum-falling-path-sum-ii.md diff --git a/articles/arithmetic-slices-ii-subsequence.md b/articles/arithmetic-slices-ii-subsequence.md new file mode 100644 index 000000000..427b1557c --- /dev/null +++ b/articles/arithmetic-slices-ii-subsequence.md @@ -0,0 +1,499 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + n = len(nums) + if n < 3: + return 0 + + INF = float('inf') + dp = {} + + def dfs(i, j, diff, flag): + if i == n: + return flag + if (i, j, diff, flag) in dp: + return dp[(i, j, diff, flag)] + + res = dfs(i + 1, j, diff, flag) + if j == -1: + res += dfs(i + 1, i, INF, flag) + else: + if diff == INF: + res += dfs(i + 1, i, nums[i] - nums[j], flag) + elif diff == nums[i] - nums[j]: + res += dfs(i + 1, i, diff, 1) + + dp[(i, j, diff, flag)] = res + return res + + return dfs(0, -1, INF, 0) +``` + +```java +public class Solution { + private static final long INF = (long) 1e15; + private Map dp = new HashMap<>(); + + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + if (n < 3) return 0; + + return dfs(nums, 0, -1, INF, 0); + } + + private int dfs(int[] nums, int i, int j, long diff, int flag) { + if (i == nums.length) { + return flag; + } + String key = i + "," + j + "," + diff + "," + flag; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0L - nums[j], flag); + } else if (diff == nums[i] - 0L - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { + static const long long INF = 1e15; + unordered_map dp; + + int dfs(vector& nums, int i, int j, long long diff, int flag) { + if (i == nums.size()) { + return flag; + } + + string key = to_string(i) + "," + to_string(j) + "," + to_string(diff) + "," + to_string(flag); + if (dp.count(key)) { + return dp[key]; + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0LL - nums[j], flag); + } else if (diff == nums[i] - 0LL - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp[key] = res; + return res; + } + +public: + int numberOfArithmeticSlices(vector& nums) { + if (nums.size() < 3) return 0; + return dfs(nums, 0, -1, INF, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + if (n < 3) return 0; + + const INF = Infinity; + const dp = new Map(); + + const dfs = (i, j, diff, flag) => { + if (i === n) { + return flag; + } + const key = `${i},${j},${diff},${flag}`; + if (dp.has(key)) { + return dp.get(key); + } + + let res = dfs(i + 1, j, diff, flag); + if (j === -1) { + res += dfs(i + 1, i, INF, flag); + } else { + if (diff === INF) { + res += dfs(i + 1, i, nums[i] - nums[j], flag); + } else if (diff === nums[i] - nums[j]) { + res += dfs(i + 1, i, diff, 1); + } + } + + dp.set(key, res); + return res; + }; + + return dfs(0, -1, INF, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 3)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + dp[i][diff] += 1 + dp[j][diff] + res += dp[j][diff] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + int res = 0; + Map[] dp = new HashMap[n]; + + for (int i = 0; i < n; i++) { + dp[i] = new HashMap<>(); + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int count = dp[j].getOrDefault(diff, 0); + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int n = nums.size(); + int res = 0; + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long) nums[i] - nums[j]; + int count = dp[j][diff]; + dp[i][diff] += count + 1; + res += count; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + let res = 0; + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const count = dp[j].get(diff) || 0; + dp[i].set(diff, (dp[i].get(diff) || 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Optimization) - I + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + s = set(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + cnt = dp[j].get(diff, 0) + if nums[i] + diff in s: + dp[i][diff] += cnt + 1 + res += cnt + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0, n = nums.length; + Set s = new HashSet<>(); + for (int num : nums) s.add(num); + + Map[] dp = new HashMap[n]; + for (int i = 0; i < n; i++) dp[i] = new HashMap<>(); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int cnt = dp[j].getOrDefault(diff, 0); + if (s.contains((int) (nums[i] + diff))) { + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0, n = nums.size(); + unordered_set s(nums.begin(), nums.end()); + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long)nums[i] - nums[j]; + int cnt = dp[j].count(diff) ? dp[j][diff] : 0; + if (s.count(nums[i] + diff)) { + dp[i][diff] += cnt + 1; + } + res += cnt; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0, n = nums.length; + const s = new Set(nums); + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const cnt = dp[j].get(diff) || 0; + if (s.has(nums[i] + diff)) { + dp[i].set(diff, (dp[i].get(diff) || 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Optimization) - II + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res = 0 + mpIdx = defaultdict(list) + n = len(nums) + dp = [[0] * n for _ in range(n)] + + for i in range(n): + mpIdx[nums[i]].append(i) + + for i in range(n): + for j in range(i): + prev = 2 * nums[j] - nums[i] + if prev in mpIdx: + for k in mpIdx[prev]: + if k >= j: + break + dp[i][j] += dp[j][k] + 1 + res += dp[i][j] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0; + Map> mpIdx = new HashMap<>(); + int n = nums.length; + int[][] dp = new int[n][n]; + + for (int i = 0; i < n; i++) { + mpIdx.putIfAbsent(nums[i], new ArrayList<>()); + mpIdx.get(nums[i]).add(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < Integer.MIN_VALUE || prev > Integer.MAX_VALUE) { + continue; + } + + if (mpIdx.containsKey((int) prev)) { + for (int k : mpIdx.get((int) prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0; + unordered_map> mpIdx; + int n = nums.size(); + vector> dp(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + mpIdx[nums[i]].push_back(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < INT_MIN || prev > INT_MAX) continue; + + if (mpIdx.count(prev)) { + for (int k : mpIdx[prev]) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0; + const mpIdx = new Map(); + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + for (let i = 0; i < n; i++) { + if (!mpIdx.has(nums[i])) mpIdx.set(nums[i], []); + mpIdx.get(nums[i]).push(i); + } + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const prev = 2 * nums[j] - nums[i]; + + if (mpIdx.has(prev)) { + for (const k of mpIdx.get(prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/cherry-pickup-ii.md b/articles/cherry-pickup-ii.md new file mode 100644 index 000000000..cc5ccc3da --- /dev/null +++ b/articles/cherry-pickup-ii.md @@ -0,0 +1,559 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + def dfs(r, c1, c2): + if c1 < 0 or c2 < 0 or c1 >= COLS or c2 >= COLS or c1 > c2: + return 0 + if r == ROWS - 1: + return grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + return res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + + private int dfs(int r, int c1, int c2, int[][] grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + + const dfs = (r, c1, c2) => { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) return 0; + if (r === ROWS - 1) { + return grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 9 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + cache = {} + + def dfs(r, c1, c2): + if (r, c1, c2) in cache: + return cache[(r, c1, c2)] + if c1 == c2 or min(c1, c2) < 0 or max(c1, c2) >= COLS: + return 0 + if r == ROWS - 1: + return grid[r][c1] + grid[r][c2] + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + cache[(r, c1, c2)] = res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + return cache[(r, c1, c2)] + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + private int[][][] cache; + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + cache = new int[ROWS][COLS][COLS]; + for (int[][] i : cache) { + for (int[] j : i) { + Arrays.fill(j, -1); + } + } + + return dfs(0, 0, COLS - 1, grid); + } + + private int dfs(int r, int c1, int c2, int[][] grid) { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= grid[0].length) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.length - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { + vector>> cache; + +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + cache.assign(ROWS, vector>(COLS, vector(COLS, -1))); + return dfs(0, 0, COLS - 1, grid); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid) { + if (min(c1, c2) < 0 || max(c1, c2) >= grid[0].size()) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.size() - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const cache = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => + Array(COLS).fill(-1) + ) + ); + + const dfs = (r, c1, c2) => { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= COLS) { + return 0; + } + if (cache[r][c1][c2] !== -1) return cache[r][c1][c2]; + if (r === ROWS - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[[0] * COLS for _ in range(COLS)] for _ in range(ROWS)] + + for r in range(ROWS - 1, -1, -1): + for c1 in range(COLS): + for c2 in range(COLS): + res = grid[r][c1] + if c1 != c2: + res += grid[r][c2] + + if r != ROWS - 1: + max_cherries = 0 + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, dp[r + 1][nc1][nc2]) + res += max_cherries + + dp[r][c1][c2] = res + + return dp[0][0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][][] dp = new int[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int dp[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => Array(COLS).fill(0)) + ); + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = 0; c2 < COLS; c2++) { + let res = grid[r][c1]; + if (c1 !== c2) { + res += grid[r][c2]; + } + + if (r !== ROWS - 1) { + let maxCherries = 0; + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[0] * COLS for _ in range(COLS)] + + for r in reversed(range(ROWS)): + cur_dp = [[0] * COLS for _ in range(COLS)] + for c1 in range(COLS): + for c2 in range(c1, COLS): + max_cherries = 0 + cherries = grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, cherries + dp[nc1][nc2]) + cur_dp[c1][c2] = max_cherries + dp = cur_dp + + return dp[0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] dp = new int[COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + int[][] cur_dp = new int[COLS][COLS]; + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> dp(COLS, vector(COLS, 0)); + + for (int r = ROWS - 1; r >= 0; r--) { + vector> cur_dp(COLS, vector(COLS, 0)); + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + + for (let r = ROWS - 1; r >= 0; r--) { + const cur_dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let maxCherries = 0; + const cherries = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + + return dp[0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/design-word-search-data-structure.md b/articles/design-word-search-data-structure.md index 2771fc3f8..501e42785 100644 --- a/articles/design-word-search-data-structure.md +++ b/articles/design-word-search-data-structure.md @@ -226,7 +226,7 @@ class WordDictionary { ### Time & Space Complexity -* Time complexity: $O(n)$ for $addWord()$, $O(m * n)$ for $search()$. +* Time complexity: $O(1)$ for $addWord()$, $O(m * n)$ for $search()$. * Space complexity: $O(m * n)$ > Where $m$ is the number of words added and $n$ is the length of the string. diff --git a/articles/freedom-trail.md b/articles/freedom-trail.md new file mode 100644 index 000000000..ca4acc077 --- /dev/null +++ b/articles/freedom-trail.md @@ -0,0 +1,142 @@ +## 1. Recursion + +::tabs-start + +```python + +``` + +```java + +``` + +```cpp + +``` + +```javascript + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python + +``` + +```java + +``` + +```cpp + +``` + +```javascript + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python + +``` + +```java + +``` + +```cpp + +``` + +```javascript + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python + +``` + +```java + +``` + +```cpp + +``` + +```javascript + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python + +``` + +```java + +``` + +```cpp + +``` + +```javascript + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/k-inverse-pairs-array.md b/articles/k-inverse-pairs-array.md new file mode 100644 index 000000000..926e484d8 --- /dev/null +++ b/articles/k-inverse-pairs-array.md @@ -0,0 +1,599 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n, k): + if n == 0: + return 1 if k == 0 else 0 + if k < 0: + return 0 + if (n, k) in cache: + return cache[(n, k)] + + cache[(n, k)] = 0 + for i in range(n): + cache[(n, k)] = (cache[(n, k)] + count(n - 1, k - i)) % MOD + + return cache[(n, k)] + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i][j] = -1; + } + } + return count(n, k); + } + + private int count(int n, int k) { + if (n == 0) return k == 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] != -1) return dp[n][k]; + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (n == 0) { + return k == 0 ? 1 : 0; + } + if (k < 0) { + return 0; + } + if (dp[n][k] != -1) { + return dp[n][k]; + } + + int res = 0; + for (int i = 0; i < n; ++i) { + res = (res + count(n - 1, k - i)) % MOD; + } + dp[n][k] = res; + return res; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (n === 0) return k === 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[-1] * (k + 1) for _ in range(n + 1)] + + def count(n, k): + if k == 0: + return 1 + if n == 1: + return 0 + if n * (n - 1) // 2 < k: + return 0 + if n * (n - 1) // 2 == k: + return 1 + if dp[n][k] != -1: + return dp[n][k] + + res = count(n, k - 1) + if k >= n: + res -= count(n - 1, k - n) + res = (res + count(n - 1, k)) % MOD + + dp[n][k] = res + return res + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(n, k); + } + + private int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k)) % MOD; + + dp[n][k] = (int) (res + MOD) % MOD; + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = int(res); + return dp[n][k]; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (k === 0) return 1; + if (n === 1) return 0; + if ((n * (n - 1)) / 2 < k) return 0; + if ((n * (n - 1)) / 2 === k) return 1; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + for pairs in range(N): + if K - pairs >= 0: + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + for (let pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 4. Dynamic Programming (Bottom-Up Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + dp[N][K] = dp[N - 1][K] + if K > 0: + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD + if K >= N: + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 5. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + prev = [0] * (k + 1) + prev[0] = 1 + + for N in range(1, n + 1): + cur = [0] * (k + 1) + total = 0 + for K in range(0, k + 1): + total = (total + prev[K]) % MOD + if K >= N: + total = (total - prev[K - N] + MOD) % MOD + cur[K] = total + prev = cur + + return prev[k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[] prev = new int[k + 1]; + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + int[] cur = new int[k + 1]; + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector prev(k + 1, 0); + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + vector cur(k + 1, 0); + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + let prev = new Array(k + 1).fill(0); + prev[0] = 1; + + for (let N = 1; N <= n; N++) { + const cur = new Array(k + 1).fill(0); + let total = 0; + for (let K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. \ No newline at end of file diff --git a/articles/minimum-difficulty-of-a-job-schedule.md b/articles/minimum-difficulty-of-a-job-schedule.md new file mode 100644 index 000000000..5738623eb --- /dev/null +++ b/articles/minimum-difficulty-of-a-job-schedule.md @@ -0,0 +1,682 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d, cur_max): + if i == n: + return 0 if d == 0 else float("inf") + if d == 0: + return float("inf") + if (i, d, cur_max) in dp: + return dp[(i, d, cur_max)] + + cur_max = max(cur_max, jobDifficulty[i]) + res = min( + dfs(i + 1, d, cur_max), + cur_max + dfs(i + 1, d - 1, -1) + ) + dp[(i, d, cur_max)] = res + return res + + return dfs(0, d, -1) +``` + +```java +public class Solution { + private int[][][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + int m = 0; + for (int i = 0; i < n; i++) { + m = Math.max(m, jobDifficulty[i]); + } + + dp = new int[n][d + 1][m + 5]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + + return dfs(0, d, -1, jobDifficulty); + } + + private int dfs(int i, int d, int curMax, int[] jobDifficulty) { + if (i == jobDifficulty.length) return d == 0 ? 0 : Integer.MAX_VALUE / 2; + if (d == 0) return Integer.MAX_VALUE / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = Math.max(curMax, jobDifficulty[i]); + int res = Math.min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector>> dp; + + int dfs(int i, int d, int curMax, const vector& jobDifficulty) { + if (i == jobDifficulty.size()) return d == 0 ? 0 : INT_MAX / 2; + if (d == 0) return INT_MAX / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = max(curMax, jobDifficulty[i]); + int res = min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int m = *max_element(jobDifficulty.begin(), jobDifficulty.end()); + dp = vector>>(n, vector>(d + 1, vector(m + 5, -1))); + return dfs(0, d, -1, jobDifficulty); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + let m = 0; + for (let it of jobDifficulty) { + m = Math.max(m, it); + } + + const dp = Array.from({ length: n }, () => + Array.from({ length: d + 1 }, () => Array(m + 5).fill(-1)) + ); + + const dfs = (i, d, curMax) => { + if (i === n) return d === 0 ? 0 : Infinity; + if (d === 0) return Infinity; + if (dp[i][d][curMax + 1] !== -1) return dp[i][d][curMax + 1]; + + const maxSoFar = Math.max(curMax, jobDifficulty[i]); + const res = Math.min( + dfs(i + 1, d, maxSoFar), + maxSoFar + dfs(i + 1, d - 1, -1) + ); + + dp[i][d][curMax + 1] = res; + return res; + }; + + return dfs(0, d, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d * m)$ +* Space complexity: $O(n * d * m)$ + +> Where $n$ is the number of jobs, $d$ is the number of days, and $m$ is the maximum difficulty value among all the job difficulties. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d): + if (i, d) in dp: + return dp[(i, d)] + + maxi = jobDifficulty[i] + if d == 1: + idx = i + while i < n: + maxi = max(maxi, jobDifficulty[i]) + i += 1 + dp[(idx, d)] = maxi + return maxi + + res = float("inf") + for j in range(i + 1, n): + res = min(res, maxi + dfs(j, d - 1)) + maxi = max(maxi, jobDifficulty[j]) + dp[(i, d)] = res + return res + + return dfs(0, d) +``` + +```java +public class Solution { + private int[][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + dp = new int[n][d + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + return dfs(0, d, jobDifficulty); + } + + private int dfs(int i, int d, int[] jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.length; + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = Integer.MAX_VALUE / 2; + for (int j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + dp.assign(n, vector(d + 1, -1)); + return dfs(0, d, jobDifficulty); + } + +private: + int dfs(int i, int d, vector& jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.size(); + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = INT_MAX / 2; + for (int j = i + 1; j < n; j++) { + res = min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n }, () => Array(d + 1).fill(-1)); + + const dfs = (i, d) => { + if (dp[i][d] !== -1) return dp[i][d]; + + let maxi = jobDifficulty[i]; + if (d === 1) { + for (let j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + let res = Infinity; + for (let j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + }; + + return dfs(0, d); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [[float("inf")] * (d + 1) for _ in range(n + 1)] + dp[n][0] = 0 + + for day in range(1, d + 1): + for i in range(n - 1, -1, -1): + maxi = 0 + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]) + + return dp[0][d] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[][] dp = new int[n + 1][d + 1]; + for (int[] row : dp) Arrays.fill(row, Integer.MAX_VALUE / 2); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector> dp(n + 1, vector(d + 1, INT_MAX / 2)); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n + 1 }, () => Array(d + 1).fill(Infinity)); + dp[n][0] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = n - 1; i >= 0; i--) { + let maxi = 0; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [float("inf")] * (n + 1) + dp[n] = 0 + + for day in range(1, d + 1): + for i in range(n - day + 1): + maxi = 0 + dp[i] = float("inf") + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i] = min(dp[i], maxi + dp[j + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int INF = Integer.MAX_VALUE / 2; + int[] dp = new int[n + 1]; + Arrays.fill(dp, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int INF = INT_MAX / 2; + vector dp(n + 1, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i] = min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = new Array(n + 1).fill(Infinity); + dp[n] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = 0; i <= n - day; i++) { + let maxi = 0; + dp[i] = Infinity; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 5. Monotonic Decreasing Stack + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + n = len(jobDifficulty) + if n < d: + return -1 + + dp = [float("inf")] * n + for day in range(1, d + 1): + next_dp = [float("inf")] * n + stack = [] + for i in range(day - 1, n): + next_dp[i] = dp[i - 1] + jobDifficulty[i] if i > 0 else jobDifficulty[i] + while stack and jobDifficulty[stack[-1]] <= jobDifficulty[i]: + j = stack.pop() + next_dp[i] = min(next_dp[i], next_dp[j] - jobDifficulty[j] + jobDifficulty[i]) + if stack: + next_dp[i] = min(next_dp[i], next_dp[stack[-1]]) + stack.append(i) + dp = next_dp + + return dp[-1] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[] dp = new int[n]; + Arrays.fill(dp, Integer.MAX_VALUE / 2); + + for (int day = 1; day <= d; day++) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE / 2); + Stack stack = new Stack<>(); + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!stack.isEmpty() && jobDifficulty[stack.peek()] <= jobDifficulty[i]) { + int j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!stack.isEmpty()) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack.peek()]); + } + stack.add(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector dp(n, INT_MAX / 2); + + for (int day = 1; day <= d; day++) { + vector nextDp(n, INT_MAX / 2); + stack st; + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!st.empty() && jobDifficulty[st.top()] <= jobDifficulty[i]) { + int j = st.top(); st.pop(); + nextDp[i] = min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!st.empty()) { + nextDp[i] = min(nextDp[i], nextDp[st.top()]); + } + st.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = Array(n).fill(Infinity); + + for (let day = 1; day <= d; day++) { + const nextDp = Array(n).fill(Infinity); + const stack = []; + for (let i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (stack.length > 0 && jobDifficulty[stack[stack.length - 1]] <= jobDifficulty[i]) { + const j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (stack.length > 0) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack[stack.length - 1]]); + } + stack.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. \ No newline at end of file diff --git a/articles/minimum-falling-path-sum-ii.md b/articles/minimum-falling-path-sum-ii.md new file mode 100644 index 000000000..b43db319c --- /dev/null +++ b/articles/minimum-falling-path-sum-ii.md @@ -0,0 +1,854 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + + def helper(r, c): + if r == N - 1: + return grid[r][c] + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + cache = {} + + def helper(r, c): + if r == N - 1: + return grid[r][c] + if (r, c) in cache: + return cache[(r, c)] + + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + cache[(r, c)] = res + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int[][] memo; + + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != Integer.MIN_VALUE) { + return memo[r][c]; + } + + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + memo = new int[N][N]; + for (int[] row : memo) { + Arrays.fill(row, Integer.MIN_VALUE); + } + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + vector> memo; + + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != INT_MIN) { + return memo[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + memo.assign(N, vector(N, INT_MIN)); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const memo = Array.from({ length: N }, () => Array(N).fill(-Infinity)); + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + if (memo[r][c] !== -Infinity) return memo[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = [[float("inf")] * N for _ in range(N)] + + for c in range(N): + dp[N - 1][c] = grid[N - 1][c] + + for r in range(N - 2, -1, -1): + for c in range(N): + for next_col in range(N): + if c != next_col: + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][next_col]) + + return min(dp[0]) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[][] dp = new int[N][N]; + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dp[0][c]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector> dp(N, vector(N, INT_MAX)); + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dp[0][c]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const dp = Array.from({ length: N }, () => Array(N).fill(Infinity)); + + for (let c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (let r = N - 2; r >= 0; r--) { + for (let c = 0; c < N; c++) { + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + return Math.min(...dp[0]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = grid[0] + + for r in range(1, N): + next_dp = [float("inf")] * N + for curr_c in range(N): + for prev_c in range(N): + if prev_c != curr_c: + next_dp[curr_c] = min( + next_dp[curr_c], + grid[r][curr_c] + dp[prev_c] + ) + dp = next_dp + + return min(dp) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[] dp = grid[0]; + + for (int r = 1; r < N; r++) { + int[] nextDp = new int[N]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + int res = Integer.MAX_VALUE; + for (int i : dp) res = Math.min(res, i); + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector dp = grid[0]; + + for (int r = 1; r < N; r++) { + vector nextDp(N, INT_MAX); + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return *min_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + let dp = grid[0]; + + for (let r = 1; r < N; r++) { + const nextDp = Array(N).fill(Infinity); + for (let currC = 0; currC < N; currC++) { + for (let prevC = 0; prevC < N; prevC++) { + if (prevC !== currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Time Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + def get_min_two(row): + two_smallest = [] + for val, idx in row: + if len(two_smallest) < 2: + two_smallest.append((val, idx)) + elif two_smallest[1][0] > val: + two_smallest.pop() + two_smallest.append((val, idx)) + two_smallest.sort() + return two_smallest + + N = len(grid) + first_row = [(val, idx) for idx, val in enumerate(grid[0])] + dp = get_min_two(first_row) + + for r in range(1, N): + next_dp = [] + for curr_c in range(N): + curr_val = grid[r][curr_c] + min_val = float("inf") + for prev_val, prev_c in dp: + if curr_c != prev_c: + min_val = min(min_val, curr_val + prev_val) + next_dp.append((min_val, curr_c)) + dp = get_min_two(next_dp) + + return min(val for val, idx in dp) +``` + +```java +public class Solution { + public List getMinTwo(List row) { + List twoSmallest = new ArrayList<>(); + for (int[] entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.add(entry); + } else if (twoSmallest.get(1)[0] > entry[0]) { + twoSmallest.remove(1); + twoSmallest.add(entry); + } + twoSmallest.sort((a, b) -> a[0] - b[0]); + } + return twoSmallest; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + + List firstRow = new ArrayList<>(); + for (int i = 0; i < grid[0].length; i++) { + firstRow.add(new int[]{grid[0][i], i}); + } + + List dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + List nextDp = new ArrayList<>(); + for (int c = 0; c < grid[0].length; c++) { + int currVal = grid[r][c]; + int minVal = Integer.MAX_VALUE; + for (int[] prev : dp) { + if (prev[1] != c) { + minVal = Math.min(minVal, currVal + prev[0]); + } + } + nextDp.add(new int[]{minVal, c}); + } + dp = getMinTwo(nextDp); + } + + return dp.stream().mapToInt(a -> a[0]).min().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + + auto getMinTwo = [](vector>& row) { + vector> twoSmallest; + for (auto& entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.push_back(entry); + } else if (twoSmallest[1].first > entry.first) { + twoSmallest.pop_back(); + twoSmallest.push_back(entry); + } + sort(twoSmallest.begin(), twoSmallest.end()); + } + return twoSmallest; + }; + + vector> firstRow; + for (int i = 0; i < grid[0].size(); i++) { + firstRow.push_back({grid[0][i], i}); + } + + vector> dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + vector> nextDp; + for (int c = 0; c < grid[0].size(); c++) { + int currVal = grid[r][c]; + int minVal = INT_MAX; + for (auto& prev : dp) { + if (prev.second != c) { + minVal = min(minVal, currVal + prev.first); + } + } + nextDp.push_back({minVal, c}); + } + dp = getMinTwo(nextDp); + } + + int result = INT_MAX; + for (auto& entry : dp) { + result = min(result, entry.first); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const getMinTwo = (row) => { + const twoSmallest = []; + for (const [val, idx] of row) { + if (twoSmallest.length < 2) { + twoSmallest.push([val, idx]); + } else if (twoSmallest[1][0] > val) { + twoSmallest.pop(); + twoSmallest.push([val, idx]); + } + twoSmallest.sort((a, b) => a[0] - b[0]); + } + return twoSmallest; + }; + + const firstRow = grid[0].map((val, idx) => [val, idx]); + let dp = getMinTwo(firstRow); + + for (let r = 1; r < N; r++) { + const nextDp = []; + for (let c = 0; c < grid[0].length; c++) { + const currVal = grid[r][c]; + let minVal = Infinity; + for (const [prevVal, prevC] of dp) { + if (c !== prevC) { + minVal = Math.min(minVal, currVal + prevVal); + } + } + nextDp.push([minVal, c]); + } + dp = getMinTwo(nextDp); + } + + return Math.min(...dp.map(([val]) => val)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + n = len(grid) + if n == 1: + return grid[0][0] + + dp_idx1 = dp_idx2 = -1 + dp_val1 = dp_val2 = 0 + + for i in range(n): + nextDp_idx1 = nextDp_idx2 = -1 + nextDp_val1 = nextDp_val2 = float("inf") + + for j in range(n): + cur = dp_val1 if j != dp_idx1 else dp_val2 + cur += grid[i][j] + + if nextDp_idx1 == -1 or cur < nextDp_val1: + nextDp_idx2, nextDp_val2 = nextDp_idx1, nextDp_val1 + nextDp_idx1, nextDp_val1 = j, cur + elif nextDp_idx2 == -1 or cur < nextDp_val2: + nextDp_idx2, nextDp_val2 = j, cur + + dp_idx1, dp_idx2, dp_val1, dp_val2 = nextDp_idx1, nextDp_idx2, nextDp_val1, nextDp_val2 + + return dp_val1 +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int n = grid.length; + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 0; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = Integer.MAX_VALUE, nextDpVal2 = Integer.MAX_VALUE; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int n = grid.size(); + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 1; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = INT_MAX, nextDpVal2 = INT_MAX; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const n = grid.length; + if (n === 1) return grid[0][0]; + + let dpIdx1 = -1, dpIdx2 = -1; + let dpVal1 = 0, dpVal2 = 0; + + for (let i = 0; i < n; i++) { + let nextDpIdx1 = -1, nextDpIdx2 = -1; + let nextDpVal1 = Infinity, nextDpVal2 = Infinity; + + for (let j = 0; j < n; j++) { + let cur = (j !== dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 === -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 === -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file From 9e1a4c51e4167efc217fa8434a2cefd3163f4cab Mon Sep 17 00:00:00 2001 From: Sri Hari Date: Sun, 29 Dec 2024 22:00:37 +0530 Subject: [PATCH 2/2] Batch-4/Neetcode-All/Added-articles --- articles/4sum.md | 647 +++++++++++++++++++++++ articles/boats-to-save-people.md | 221 ++++++++ articles/concatenation-of-array.md | 4 +- articles/find-k-closest-elements.md | 555 ++++++++++++++++++++ articles/freedom-trail.md | 763 ++++++++++++++++++++++++++-- articles/rotate-array.md | 433 ++++++++++++++++ 6 files changed, 2591 insertions(+), 32 deletions(-) create mode 100644 articles/4sum.md create mode 100644 articles/boats-to-save-people.md create mode 100644 articles/find-k-closest-elements.md create mode 100644 articles/rotate-array.md diff --git a/articles/4sum.md b/articles/4sum.md new file mode 100644 index 000000000..f99c11ddd --- /dev/null +++ b/articles/4sum.md @@ -0,0 +1,647 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + n = len(nums) + nums.sort() + res = set() + + for a in range(n): + for b in range(a + 1, n): + for c in range(b + 1, n): + for d in range(c + 1, n): + if nums[a] + nums[b] + nums[c] + nums[d] == target: + res.add((nums[a], nums[b], nums[c], nums[d])) + return list(res) +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + int n = nums.length; + Arrays.sort(nums); + Set> res = new HashSet<>(); + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0L + nums[c] + nums[d] == target) { + res.add(Arrays.asList(nums[a], nums[b], nums[c], nums[d])); + } + } + } + } + } + + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + set> res; + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0LL + nums[c] + nums[d] == target) { + res.insert({nums[a], nums[b], nums[c], nums[d]}); + } + } + } + } + } + + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + let n = nums.length; + nums.sort((a, b) => a - b); + let res = new Set(); + + for (let a = 0; a < n; a++) { + for (let b = a + 1; b < n; b++) { + for (let c = b + 1; c < n; c++) { + for (let d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + nums[c] + nums[d] === target) { + res.add(JSON.stringify([nums[a], nums[b], nums[c], nums[d]])); + } + } + } + } + } + + return Array.from(res).map(JSON.parse); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + count = defaultdict(int) + for num in nums: + count[num] += 1 + + res = [] + for i in range(len(nums)): + count[nums[i]] -= 1 + if i > 0 and nums[i] == nums[i - 1]: + continue + + for j in range(i + 1, len(nums)): + count[nums[j]] -= 1 + if j > i + 1 and nums[j] == nums[j - 1]: + continue + + for k in range(j + 1, len(nums)): + count[nums[k]] -= 1 + if k > j + 1 and nums[k] == nums[k - 1]: + continue + + fourth = target - (nums[i] + nums[j] + nums[k]) + if count[fourth] > 0: + res.append([nums[i], nums[j], nums[k], fourth]) + + for k in range(j + 1, len(nums)): + count[nums[k]] += 1 + + for j in range(i + 1, len(nums)): + count[nums[j]] += 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + List> res = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + count.put(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long fourth = target - (nums[i] + nums[j] + 0L + nums[k]); + if (fourth > Integer.MAX_VALUE || fourth < Integer.MIN_VALUE) { + continue; + } + if (count.getOrDefault((int) fourth, 0) > 0) { + res.add(Arrays.asList(nums[i], nums[j], nums[k], (int) fourth)); + } + } + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) + 1); + } + } + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + sort(nums.begin(), nums.end()); + unordered_map count; + for (int num : nums) { + count[num]++; + } + vector> res; + + for (int i = 0; i < nums.size(); i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]--; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long long fourth = target - (nums[i] + nums[j] + 0LL + nums[k]); + if (fourth < INT_MIN || fourth > INT_MAX) continue; + if (count[fourth] > 0) { + res.push_back({nums[i], nums[j], nums[k], int(fourth)}); + } + } + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]++; + } + } + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + const res = []; + + for (let i = 0; i < nums.length; i++) { + count.set(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] === nums[k - 1]) continue; + + const fourth = target - (nums[i] + nums[j] + nums[k]); + if ((count.get(fourth) || 0) > 0) { + res.push([nums[i], nums[j], nums[k], fourth]); + } + } + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) + 1); + } + } + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(n)$ space for the hash map. + * $O(m)$ space for the output array. + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + n = len(nums) + res = [] + + for i in range(n): + if i > 0 and nums[i] == nums[i - 1]: + continue + for j in range(i + 1, n): + if j > i + 1 and nums[j] == nums[j - 1]: + continue + left, right = j + 1, n - 1 + while left < right: + total = nums[i] + nums[j] + nums[left] + nums[right] + if total == target: + res.append([nums[i], nums[j], nums[left], nums[right]]) + left += 1 + right -= 1 + while left < right and nums[left] == nums[left - 1]: + left += 1 + while left < right and nums[right] == nums[right + 1]: + right -= 1 + elif total < target: + left += 1 + else: + right -= 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long sum = (long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + vector> res; + int n = nums.size(); + sort(nums.begin(), nums.end()); + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long long sum = (long long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.push_back({nums[i], nums[j], nums[left], nums[right]}); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + let left = j + 1, right = n - 1; + while (left < right) { + const sum = nums[i] + nums[j] + nums[left] + nums[right]; + if (sum === target) { + res.push([nums[i], nums[j], nums[left], nums[right]]); + left++; + right--; + while (left < right && nums[left] === nums[left - 1]) left++; + while (left < right && nums[right] === nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. + +--- + +## 4. K-Sum + Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + res, quad = [], [] + + def kSum(k, start, target): + if k == 2: + l, r = start, len(nums) - 1 + while l < r: + if nums[l] + nums[r] < target: + l += 1 + elif nums[l] + nums[r] > target: + r -= 1 + else: + res.append(quad + [nums[l], nums[r]]) + l += 1 + r -= 1 + while l < r and nums[l] == nums[l - 1]: + l += 1 + while l < r and nums[r] == nums[r + 1]: + r -= 1 + return + + for i in range(start, len(nums) - k + 1): + if i > start and nums[i] == nums[i - 1]: + continue + quad.append(nums[i]) + kSum(k - 1, i + 1, target - nums[i]) + quad.pop() + + kSum(4, 0, target) + return res +``` + +```java +public class Solution { + private List> res; + private List quad; + + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + res = new ArrayList<>(); + quad = new ArrayList<>(); + kSum(nums, 4, 0, target); + return res; + } + + private void kSum(int[] nums, int k, int start, long target) { + if (k == 2) { + int l = start, r = nums.length - 1; + while (l < r) { + long sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.add(new ArrayList<>(quad)); + res.get(res.size() - 1).add(nums[l]); + res.get(res.size() - 1).add(nums[r]); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.add(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.remove(quad.size() - 1); + } + } +} +``` + +```cpp +class Solution { + vector> res; + vector quad; + +public: + vector> fourSum(vector& nums, int target) { + if (nums.size() < 4) return {}; + sort(nums.begin(), nums.end()); + kSum(nums, 4, 0, (long long) target); + return res; + } + +private: + void kSum(vector& nums, int k, int start, long long target) { + if (k == 2) { + int l = start, r = nums.size() - 1; + while (l < r) { + long long sum = (long long) nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + quad.push_back(nums[l]); + quad.push_back(nums[r]); + res.push_back(quad); + quad.pop_back(); + quad.pop_back(); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.size() - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.push_back(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const quad = []; + + const kSum = (k, start, target) => { + if (k === 2) { + let l = start, r = nums.length - 1; + while (l < r) { + const sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.push([...quad, nums[l], nums[r]]); + l++; + r--; + while (l < r && nums[l] === nums[l - 1]) l++; + while (l < r && nums[r] === nums[r + 1]) r--; + } + } + return; + } + + for (let i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] === nums[i - 1]) continue; + quad.push(nums[i]); + kSum(k - 1, i + 1, target - nums[i]); + quad.pop(); + } + }; + + kSum(4, 0, target); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. \ No newline at end of file diff --git a/articles/boats-to-save-people.md b/articles/boats-to-save-people.md new file mode 100644 index 000000000..8abd6a9a7 --- /dev/null +++ b/articles/boats-to-save-people.md @@ -0,0 +1,221 @@ +## 1. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + people.sort() + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + Arrays.sort(people); + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + sort(people.begin(), people.end()); + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + people.sort((a, b) => a - b); + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + let remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + m = max(people) + count = [0] * (m + 1) + for p in people: + count[p] += 1 + + idx, i = 0, 1 + while idx < len(people): + while count[i] == 0: + i += 1 + people[idx] = i + count[i] -= 1 + idx += 1 + + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + int m = Arrays.stream(people).max().getAsInt(); + int[] count = new int[m + 1]; + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.length) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + int m = *max_element(people.begin(), people.end()); + vector count(m + 1, 0); + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.size()) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + const m = Math.max(...people); + const count = new Array(m + 1).fill(0); + for (const p of people) { + count[p]++; + } + + let idx = 0, i = 1; + while (idx < people.length) { + while (count[i] === 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + const remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/concatenation-of-array.md b/articles/concatenation-of-array.md index 24e788da2..3ea2a1e68 100644 --- a/articles/concatenation-of-array.md +++ b/articles/concatenation-of-array.md @@ -65,7 +65,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: $O(n)$ for the output array. --- @@ -132,4 +132,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/find-k-closest-elements.md b/articles/find-k-closest-elements.md new file mode 100644 index 000000000..d4a177c89 --- /dev/null +++ b/articles/find-k-closest-elements.md @@ -0,0 +1,555 @@ +## 1. Sorting (Custom Comparator) + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + arr.sort(key=lambda num: (abs(num - x), num)) + return sorted(arr[:k]) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + List list = new ArrayList<>(); + for (int num : arr) { + list.add(num); + } + + list.sort((a, b) -> { + int diff = Math.abs(a - x) - Math.abs(b - x); + return diff == 0 ? Integer.compare(a, b) : diff; + }); + + List result = list.subList(0, k); + Collections.sort(result); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + sort(arr.begin(), arr.end(), [x](int a, int b) { + int diff = abs(a - x) - abs(b - x); + return diff == 0 ? a < b : diff < 0; + }); + vector result(arr.begin(), arr.begin() + k); + sort(result.begin(), result.end()); + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + arr.sort((a, b) => { + const diff = Math.abs(a - x) - Math.abs(b - x); + return diff === 0 ? a - b : diff; + }); + const result = arr.slice(0, k); + return result.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 2. Linear Scan + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + n = len(arr) + idx = 0 + for i in range(1, n): + if abs(x - arr[idx]) > abs(x - arr[i]): + idx = i + + res = [arr[idx]] + l, r = idx - 1, idx + 1 + + while len(res) < k: + if l >= 0 and r < n: + if abs(x - arr[l]) <= abs(x - arr[r]): + res.append(arr[l]) + l -= 1 + else: + res.append(arr[r]) + r += 1 + elif l >= 0: + res.append(arr[l]) + l -= 1 + elif r < n: + res.append(arr[r]) + r += 1 + + return sorted(res) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int n = arr.length; + int idx = 0; + for (int i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + List res = new ArrayList<>(); + res.add(arr[idx]); + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.add(arr[l--]); + } else { + res.add(arr[r++]); + } + } else if (l >= 0) { + res.add(arr[l--]); + } else if (r < n) { + res.add(arr[r++]); + } + } + + Collections.sort(res); + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int n = arr.size(); + int idx = 0; + for (int i = 1; i < n; i++) { + if (abs(x - arr[idx]) > abs(x - arr[i])) { + idx = i; + } + } + + vector res = {arr[idx]}; + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + res.push_back(arr[l--]); + } else { + res.push_back(arr[r++]); + } + } else if (l >= 0) { + res.push_back(arr[l--]); + } else if (r < n) { + res.push_back(arr[r++]); + } + } + + sort(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + const n = arr.length; + let idx = 0; + for (let i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + const res = [arr[idx]]; + let l = idx - 1, r = idx + 1; + + while (res.length < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.push(arr[l--]); + } else { + res.push(arr[r++]); + } + } else if (l >= 0) { + res.push(arr[l--]); + } else if (r < n) { + res.push(arr[r++]); + } + } + + return res.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(k)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while r - l >= k: + if abs(x - arr[l]) <= abs(x - arr[r]): + r -= 1 + else: + l += 1 + + return arr[l: r + 1] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + List result = new ArrayList<>(); + for (int i = l; i <= r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (r - l >= k) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return vector(arr.begin() + l, arr.begin() + r + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return arr.slice(l, r + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n - k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 4. Binary Search + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while l < r: + mid = (l + r) // 2 + if arr[mid] < x: + l = mid + 1 + else: + r = mid + + l, r = l - 1, l + while r - l - 1 < k: + if l < 0: + r += 1 + elif r >= len(arr): + l -= 1 + elif abs(arr[l] - x) <= abs(arr[r] - x): + l -= 1 + else: + r += 1 + + return arr[l + 1:r] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + List result = new ArrayList<>(); + for (int i = l + 1; i < r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.size()) { + l--; + } else if (abs(arr[l] - x) <= abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return vector(arr.begin() + l + 1, arr.begin() + r); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return arr.slice(l + 1, r); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - k + while l < r: + m = (l + r) // 2 + if x - arr[m] > arr[m + k] - x: + l = m + 1 + else: + r = m + return arr[l:l + k] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + List result = new ArrayList<>(); + for (int i = l; i < l + k; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return vector(arr.begin() + l, arr.begin() + l + k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - k; + while (l < r) { + const m = Math.floor((l + r) / 2); + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return arr.slice(l, l + k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (n - k) + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. \ No newline at end of file diff --git a/articles/freedom-trail.md b/articles/freedom-trail.md index ca4acc077..9c7ef1c89 100644 --- a/articles/freedom-trail.md +++ b/articles/freedom-trail.md @@ -3,27 +3,103 @@ ::tabs-start ```python - +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + def dfs(r, k): + if k == len(key): + return 0 + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), len(ring) - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + return res + + return dfs(0, 0) ``` ```java - +public class Solution { + public int findRotateSteps(String ring, String key) { + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +} ``` ```cpp - +class Solution { +public: + int findRotateSteps(string ring, string key) { + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, const string& ring, const string& key) { + if (k == key.size()) return 0; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +}; ``` ```javascript - +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const dfs = (r, k) => { + if (k === key.length) return 0; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + return res; + }; + + return dfs(0, 0); + } +} ``` ::tabs-end ### Time & Space Complexity -* Time complexity: $O(2 ^ n)$ -* Space complexity: $O(n)$ +* Time complexity: $O(n ^ m)$ +* Space complexity: $O(m)$ for recursion stack. + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. --- @@ -32,27 +108,136 @@ ::tabs-start ```python - +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = {} + + def dfs(r, k): + if k == m: + return 0 + if (r, k) in dp: + return dp[(r, k)] + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + dp[(r, k)] = res + return res + + return dfs(0, 0) ``` ```java - +public class Solution { + private int[][] dp; + + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + dp = new int[n][m]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +} ``` ```cpp - +class Solution { + vector> dp; + +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + dp.assign(n, vector(m, -1)); + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, string& ring, string& key) { + if (k == key.size()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +}; ``` ```javascript - +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: n }, () => Array(m).fill(-1)); + + const dfs = (r, k) => { + if (k === key.length) return 0; + if (dp[r][k] !== -1) return dp[r][k]; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + + dp[r][k] = res; + return res; + }; + + return dfs(0, 0); + } +} ``` ::tabs-end ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. --- @@ -61,82 +246,600 @@ ::tabs-start ```python - +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [[float("inf")] * n for _ in range(m + 1)] + + for i in range(n): + dp[m][i] = 0 + + for k in range(m - 1, -1, -1): + for r in range(n): + for i in range(n): + if ring[i] == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + dp[k][r] = min(dp[k][r], min_dist + 1 + dp[k + 1][i]) + + return dp[0][0] ``` ```java - +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[][] dp = new int[m + 1][n]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = Integer.MAX_VALUE; + } + } + + for (int i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; k--) { + for (int r = 0; r < n; r++) { + for (int i = 0; i < n; i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} ``` ```cpp - +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector> dp(m + 1, vector(n, INT_MAX)); + + for (int i = 0; i < n; ++i) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; --k) { + for (int r = 0; r < n; ++r) { + for (int i = 0; i < n; ++i) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), n - abs(r - i)); + dp[k][r] = min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +}; ``` ```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: m + 1 }, () => + Array(n).fill(Infinity) + ); + + for (let i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (let k = m - 1; k >= 0; k--) { + for (let r = 0; r < n; r++) { + for (let i = 0; i < n; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 4. Dynamic Programming (Space Optimized) - I +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [0] * n + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + next_dp = [float("inf")] * n + for r in range(n): + for i in adj[ord(key[k]) - ord('a')]: + min_dist = min(abs(r - i), n - abs(r - i)) + next_dp[r] = min(next_dp[r], min_dist + 1 + dp[i]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + for (int r = 0; r < n; r++) { + for (int i : adj[key.charAt(k) - 'a']) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector dp(n, 0); + + vector> adj(26); + for (int i = 0; i < n; ++i) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; --k) { + vector nextDp(n, INT_MAX); + for (int r = 0; r < n; ++r) { + for (int& i : adj[key[k] - 'a']) { + int minDist = min(abs(r - i), n - abs(r - i)); + nextDp[r] = min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + let dp = new Array(n).fill(0); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const nextDp = new Array(n).fill(Infinity); + for (let r = 0; r < n; r++) { + for (const i of adj[key.charCodeAt(k) - 97]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} ``` ::tabs-end ### Time & Space Complexity -* Time complexity: $O(n)$ +* Time complexity: $O(n ^ 2 * m)$ * Space complexity: $O(n)$ +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + --- -## 4. Dynamic Programming (Space Optimized) +## 5. Dynamic Programming (Space optimized) - II ::tabs-start ```python - +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [min(i, n - i) for i in range(n)] + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(1, m): + for r in adj[ord(key[k]) - ord('a')]: + min_dist = float("inf") + for i in adj[ord(key[k - 1]) - ord('a')]: + min_dist = min( + min_dist, + min(abs(r - i), n - abs(r - i)) + dp[i] + ) + dp[r] = min_dist + + return min(dp[i] for i in adj[ord(key[-1]) - ord('a')]) + m ``` ```java - +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + for (int i = 0; i < n; i++) { + dp[i] = Math.min(i, n - i); + } + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key.charAt(k) - 'a']) { + int minDist = Integer.MAX_VALUE; + for (int i : adj[key.charAt(k - 1) - 'a']) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + int result = Integer.MAX_VALUE; + for (int i : adj[key.charAt(m - 1) - 'a']) { + result = Math.min(result, dp[i]); + } + + return result + m; + } +} ``` ```cpp - +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + vector dp(n); + + for (int i = 0; i < n; i++) { + dp[i] = min(i, n - i); + } + + vector> adj(26); + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key[k] - 'a']) { + int minDist = INT_MAX; + for (int i : adj[key[k - 1] - 'a']) { + minDist = min(minDist, min(abs(r - i), n - abs(r - i)) + dp[i]); + } + dp[r] = minDist; + } + } + + int result = INT_MAX; + for (int& i : adj[key[m - 1] - 'a']) { + result = min(result, dp[i]); + } + + return result + m; + } +}; ``` ```javascript - +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array(n).fill(0).map((_, i) => Math.min(i, n - i)); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = 1; k < m; k++) { + for (let r of adj[key.charCodeAt(k) - 97]) { + let minDist = Infinity; + for (let i of adj[key.charCodeAt(k - 1) - 97]) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + return Math.min(...adj[key.charCodeAt(m - 1) - 97].map(i => dp[i])) + m; + } +} ``` ::tabs-end ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. --- -## 5. Dynamic Programming (Optimal) +## 6. Dynamic Programming (Optimal) ::tabs-start ```python - +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + + dp = [0] * n + next_dp = [0] * n + + adj = [[] for _ in range(26)] + for i, c in enumerate(ring): + adj[ord(c) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + c = ord(key[k]) - ord('a') + it, N = 0, len(adj[c]) + + for r in range(n): + if ord(ring[r]) - ord('a') != c: + next_dp[r] = float('inf') + while it < N and adj[c][it] < r: + it += 1 + + nextIdx = adj[c][it] if it < N else adj[c][0] + prevIdx = adj[c][it - 1] if it > 0 else adj[c][-1] + + next_dp[r] = min( + (r - prevIdx if r > prevIdx else n - (prevIdx - r)) + dp[prevIdx], + (nextIdx - r if nextIdx > r else n - (r - nextIdx)) + dp[nextIdx] + ) + else: + next_dp[r] = dp[r] + + dp, next_dp = next_dp, dp + + return dp[0] + m ``` ```java - +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + + int[] dp = new int[n]; + int[] nextDp = new int[n]; + List[] adj = new ArrayList[26]; + + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key.charAt(k) - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring.charAt(r) - 'a' != c) { + nextDp[r] = Integer.MAX_VALUE; + while (it < N && adj[c].get(it) < r) { + it++; + } + + int nextIdx = it < N ? adj[c].get(it) : adj[c].get(0); + int prevIdx = it > 0 ? adj[c].get(it - 1) : adj[c].get(N - 1); + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + int[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0] + m; + } +} ``` ```cpp - +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + + vector dp(n, 0); + vector nextDp(n, 0); + vector> adj(26); + + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key[k] - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring[r] - 'a' != c) { + nextDp[r] = INT_MAX; + while (it < N && adj[c][it] < r) { + it++; + } + + int nextIdx = it < N ? adj[c][it] : adj[c][0]; + int prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + dp.swap(nextDp); + } + + return dp[0] + m; + } +}; ``` ```javascript - +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length, m = key.length; + + let dp = Array(n).fill(0); + let nextDp = Array(n).fill(0); + const adj = Array.from({ length: 26 }, () => []); + + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const c = key.charCodeAt(k) - 97; + let it = 0, N = adj[c].length; + + for (let r = 0; r < n; r++) { + if (ring.charCodeAt(r) - 97 !== c) { + nextDp[r] = Infinity; + while (it < N && adj[c][it] < r) { + it++; + } + + const nextIdx = it < N ? adj[c][it] : adj[c][0]; + const prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + [dp, nextDp] = [nextDp, dp]; + } + + return dp[0] + m; + } +} ``` ::tabs-end ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. \ No newline at end of file diff --git a/articles/rotate-array.md b/articles/rotate-array.md new file mode 100644 index 000000000..72dfcb037 --- /dev/null +++ b/articles/rotate-array.md @@ -0,0 +1,433 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + while k: + tmp = nums[n - 1] + for i in range(n - 1, 0, -1): + nums[i] = nums[i - 1] + nums[0] = tmp + k -= 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + while (k > 0) { + const tmp = nums[n - 1]; + for (let i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Extra Space + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + tmp = [0] * n + for i in range(n): + tmp[(i + k) % n] = nums[i] + + nums[:] = tmp +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] tmp = new int[n]; + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + vector tmp(n); + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + const tmp = new Array(n); + for (let i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (let i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 3. Cyclic Traversal + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + count = start = 0 + + while count < n: + current = start + prev = nums[start] + while True: + next_idx = (current + k) % n + nums[next_idx], prev = prev, nums[next_idx] + current = next_idx + count += 1 + + if start == current: + break + start += 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + let count = 0; + + for (let start = 0; count < n; start++) { + let current = start; + let prev = nums[start]; + do { + const nextIdx = (current + k) % n; + const temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start !== current); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Using Reverse + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + + def reverse(l: int, r: int) -> None: + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + reverse(0, n - 1) + reverse(0, k - 1) + reverse(k, n - 1) +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + + private void reverse(int[] nums, int l, int r) { + while (l < r) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + +private: + void reverse(vector& nums, int l, int r) { + while (l < r) { + swap(nums[l], nums[r]); + l++; + r--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + + const reverse = (l, r) => { + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + r--; + } + }; + + reverse(0, n - 1); + reverse(0, k - 1); + reverse(k, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. One Liner + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + nums[:] = nums[-k % len(nums):] + nums[:-k % len(nums)] +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] rotated = Arrays.copyOfRange(nums, n - k % n, n); + System.arraycopy(nums, 0, nums, k % n, n - k % n); + System.arraycopy(rotated, 0, nums, 0, rotated.length); + } +} +``` + +```cpp + +class Solution { +public: + void rotate(vector& nums, int k) { + std::rotate(nums.begin(), nums.end() - (k % nums.size()), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + nums.splice(0, 0, ...nums.splice(nums.length - (k % nums.length))); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file