From b1d77668742167ecd3557e1be795c9d418acb16f Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sat, 23 Mar 2024 09:18:53 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.3048 No.3048.Earliest Second to Mark Indices I --- .../README.md | 186 +++++++++++++++--- .../README_EN.md | 184 ++++++++++++++--- .../Solution.cpp | 39 ++++ .../Solution.go | 26 +++ .../Solution.java | 55 +++--- .../Solution.py | 21 ++ .../Solution.ts | 33 ++++ 7 files changed, 456 insertions(+), 88 deletions(-) create mode 100644 solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.cpp create mode 100644 solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.go create mode 100644 solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.py create mode 100644 solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.ts diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/README.md b/solution/3000-3099/3048.Earliest Second to Mark Indices I/README.md index 9bdb27c2670cd..71ca66e5063a2 100644 --- a/solution/3000-3099/3048.Earliest Second to Mark Indices I/README.md +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/README.md @@ -82,68 +82,194 @@ ## 解法 -### 方法一 +### 方法一:二分查找 + +我们注意到,如果我们能够在 $t$ 秒内标记所有下标,那么我们也能在 $t' \geq t$ 秒内标记所有下标。因此,我们可以使用二分查找的方法找到最早的秒数。 + +我们定义二分查找的左右边界分别为 $l = 1$ 和 $r = m + 1$,其中 $m$ 是数组 `changeIndices` 的长度。对于每一个 $t = \frac{l + r}{2}$,我们检查是否能在 $t$ 秒内标记所有下标。如果能,我们将右边界移动到 $t$,否则我们将左边界移动到 $t + 1$。最终,我们判定左边界是否大于 $m$,如果是则返回 $-1$,否则返回左边界。 + +题目的关键在于如何判断是否能在 $t$ 秒内标记所有下标。我们可以使用一个数组 $last$ 记录每一个下标最晚需要被标记的时间,用一个变量 $decrement$ 记录当前可以减少的次数,用一个变量 $marked$ 记录已经被标记的下标的数量。 + +我们遍历数组 `changeIndices` 的前 $t$ 个元素,对于每一个元素 $i$,如果 $last[i] = s$,那么我们需要检查 $decrement$ 是否大于等于 $nums[i - 1]$,如果是,我们将 $decrement$ 减去 $nums[i - 1]$,并且将 $marked$ 加一;否则,我们返回 `False`。如果 $last[i] \neq s$,那么我们可以暂时不标记下标,因此将 $decrement$ 加一。最后,我们检查 $marked$ 是否等于 $n$,如果是,我们返回 `True`,否则返回 `False`。 + +时间复杂度 $O(m \times \log m)$,空间复杂度 $O(n)$。其中 $n$ 和 $m$ 分别是数组 `nums` 和 `changeIndices` 的长度。 ```python - +class Solution: + def earliestSecondToMarkIndices( + self, nums: List[int], changeIndices: List[int] + ) -> int: + def check(t: int) -> bool: + decrement = 0 + marked = 0 + last = {i: s for s, i in enumerate(changeIndices[:t])} + for s, i in enumerate(changeIndices[:t]): + if last[i] == s: + if decrement < nums[i - 1]: + return False + decrement -= nums[i - 1] + marked += 1 + else: + decrement += 1 + return marked == len(nums) + + m = len(changeIndices) + l = bisect_left(range(1, m + 2), True, key=check) + 1 + return -1 if l > m else l ``` ```java class Solution { + private int[] nums; + private int[] changeIndices; + public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) { - int l = 0; - int r = changeIndices.length + 1; + this.nums = nums; + this.changeIndices = changeIndices; + int m = changeIndices.length; + int l = 1, r = m + 1; while (l < r) { - final int m = (l + r) / 2; - if (canMark(nums, changeIndices, m)) { - r = m; + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; } else { - l = m + 1; + l = mid + 1; } } - return l <= changeIndices.length ? l : -1; + return l > m ? -1 : l; } - private boolean canMark(int[] nums, int[] changeIndices, int second) { - int numMarked = 0; - int decrement = 0; - // indexToLastSecond[i] := the last second to mark the index i - int[] indexToLastSecond = new int[nums.length]; - Arrays.fill(indexToLastSecond, -1); - - for (int i = 0; i < second; ++i) { - indexToLastSecond[changeIndices[i] - 1] = i; + private boolean check(int t) { + int[] last = new int[nums.length + 1]; + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; } - - for (int i = 0; i < second; ++i) { - // Convert to 0-indexed. - final int index = changeIndices[i] - 1; - if (i == indexToLastSecond[index]) { - // Reach the last occurrence of the number. - // So, the current second will be used to mark the index. - if (nums[index] > decrement) { - // The decrement is less than the number to be marked. + int decrement = 0; + int marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { return false; } - decrement -= nums[index]; - ++numMarked; + decrement -= nums[i - 1]; + ++marked; } else { ++decrement; } } - return numMarked == nums.length; + return marked == nums.length; } } ``` ```cpp +class Solution { +public: + int earliestSecondToMarkIndices(vector& nums, vector& changeIndices) { + int n = nums.size(); + int last[n + 1]; + auto check = [&](int t) { + memset(last, 0, sizeof(last)); + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + int decrement = 0, marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked == n; + }; + int m = changeIndices.size(); + int l = 1, r = m + 1; + while (l < r) { + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; + } +}; ``` ```go +func earliestSecondToMarkIndices(nums []int, changeIndices []int) int { + n, m := len(nums), len(changeIndices) + l := sort.Search(m+1, func(t int) bool { + last := make([]int, n+1) + for s, i := range changeIndices[:t] { + last[i] = s + } + decrement, marked := 0, 0 + for s, i := range changeIndices[:t] { + if last[i] == s { + if decrement < nums[i-1] { + return false + } + decrement -= nums[i-1] + marked++ + } else { + decrement++ + } + } + return marked == n + }) + if l > m { + return -1 + } + return l +} +``` +```ts +function earliestSecondToMarkIndices(nums: number[], changeIndices: number[]): number { + const [n, m] = [nums.length, changeIndices.length]; + let [l, r] = [1, m + 1]; + const check = (t: number): boolean => { + const last: number[] = Array(n + 1).fill(0); + for (let s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + let [decrement, marked] = [0, 0]; + for (let s = 0; s < t; ++s) { + const i = changeIndices[s]; + if (last[i] === s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked === n; + }; + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; +} ``` diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/README_EN.md b/solution/3000-3099/3048.Earliest Second to Mark Indices I/README_EN.md index 4ea3cbf410bc5..fd657b2149fac 100644 --- a/solution/3000-3099/3048.Earliest Second to Mark Indices I/README_EN.md +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/README_EN.md @@ -78,68 +78,194 @@ Hence, the answer is -1. ## Solutions -### Solution 1 +### Solution 1: Binary Search + +We notice that if we can mark all indices within $t$ seconds, then we can also mark all indices within $t' \geq t$ seconds. Therefore, we can use binary search to find the earliest seconds. + +We define the left and right boundaries of binary search as $l = 1$ and $r = m + 1$, where $m$ is the length of the array `changeIndices`. For each $t = \frac{l + r}{2}$, we check whether we can mark all indices within $t$ seconds. If we can, we move the right boundary to $t$, otherwise we move the left boundary to $t + 1$. Finally, we judge whether the left boundary is greater than $m$, if it is, return $-1$, otherwise return the left boundary. + +The key to the problem is how to judge whether we can mark all indices within $t$ seconds. We can use an array $last$ to record the latest time each index needs to be marked, use a variable $decrement$ to record the current number of times that can be reduced, and use a variable $marked$ to record the number of indices that have been marked. + +We traverse the first $t$ elements of the array `changeIndices`, for each element $i$, if $last[i] = s$, then we need to check whether $decrement$ is greater than or equal to $nums[i - 1]$, if it is, we subtract $nums[i - 1]$ from $decrement$, and add one to $marked$; otherwise, we return `False`. If $last[i] \neq s$, then we can temporarily not mark the index, so we add one to $decrement$. Finally, we check whether $marked$ is equal to $n$, if it is, we return `True`, otherwise return `False`. + +The time complexity is $O(m \times \log m)$, and the space complexity is $O(n)$. Where $n$ and $m$ are the lengths of `nums` and `changeIndices` respectively. ```python +class Solution: + def earliestSecondToMarkIndices( + self, nums: List[int], changeIndices: List[int] + ) -> int: + def check(t: int) -> bool: + decrement = 0 + marked = 0 + last = {i: s for s, i in enumerate(changeIndices[:t])} + for s, i in enumerate(changeIndices[:t]): + if last[i] == s: + if decrement < nums[i - 1]: + return False + decrement -= nums[i - 1] + marked += 1 + else: + decrement += 1 + return marked == len(nums) + m = len(changeIndices) + l = bisect_left(range(1, m + 2), True, key=check) + 1 + return -1 if l > m else l ``` ```java class Solution { + private int[] nums; + private int[] changeIndices; + public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) { - int l = 0; - int r = changeIndices.length + 1; + this.nums = nums; + this.changeIndices = changeIndices; + int m = changeIndices.length; + int l = 1, r = m + 1; while (l < r) { - final int m = (l + r) / 2; - if (canMark(nums, changeIndices, m)) { - r = m; + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; } else { - l = m + 1; + l = mid + 1; } } - return l <= changeIndices.length ? l : -1; + return l > m ? -1 : l; } - private boolean canMark(int[] nums, int[] changeIndices, int second) { - int numMarked = 0; - int decrement = 0; - // indexToLastSecond[i] := the last second to mark the index i - int[] indexToLastSecond = new int[nums.length]; - Arrays.fill(indexToLastSecond, -1); - - for (int i = 0; i < second; ++i) { - indexToLastSecond[changeIndices[i] - 1] = i; + private boolean check(int t) { + int[] last = new int[nums.length + 1]; + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; } - - for (int i = 0; i < second; ++i) { - // Convert to 0-indexed. - final int index = changeIndices[i] - 1; - if (i == indexToLastSecond[index]) { - // Reach the last occurrence of the number. - // So, the current second will be used to mark the index. - if (nums[index] > decrement) { - // The decrement is less than the number to be marked. + int decrement = 0; + int marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { return false; } - decrement -= nums[index]; - ++numMarked; + decrement -= nums[i - 1]; + ++marked; } else { ++decrement; } } - return numMarked == nums.length; + return marked == nums.length; } } ``` ```cpp +class Solution { +public: + int earliestSecondToMarkIndices(vector& nums, vector& changeIndices) { + int n = nums.size(); + int last[n + 1]; + auto check = [&](int t) { + memset(last, 0, sizeof(last)); + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + int decrement = 0, marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked == n; + }; + int m = changeIndices.size(); + int l = 1, r = m + 1; + while (l < r) { + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; + } +}; ``` ```go +func earliestSecondToMarkIndices(nums []int, changeIndices []int) int { + n, m := len(nums), len(changeIndices) + l := sort.Search(m+1, func(t int) bool { + last := make([]int, n+1) + for s, i := range changeIndices[:t] { + last[i] = s + } + decrement, marked := 0, 0 + for s, i := range changeIndices[:t] { + if last[i] == s { + if decrement < nums[i-1] { + return false + } + decrement -= nums[i-1] + marked++ + } else { + decrement++ + } + } + return marked == n + }) + if l > m { + return -1 + } + return l +} +``` +```ts +function earliestSecondToMarkIndices(nums: number[], changeIndices: number[]): number { + const [n, m] = [nums.length, changeIndices.length]; + let [l, r] = [1, m + 1]; + const check = (t: number): boolean => { + const last: number[] = Array(n + 1).fill(0); + for (let s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + let [decrement, marked] = [0, 0]; + for (let s = 0; s < t; ++s) { + const i = changeIndices[s]; + if (last[i] === s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked === n; + }; + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; +} ``` diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.cpp b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.cpp new file mode 100644 index 0000000000000..7a53dbf812554 --- /dev/null +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.cpp @@ -0,0 +1,39 @@ +class Solution { +public: + int earliestSecondToMarkIndices(vector& nums, vector& changeIndices) { + int n = nums.size(); + int last[n + 1]; + auto check = [&](int t) { + memset(last, 0, sizeof(last)); + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + int decrement = 0, marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked == n; + }; + + int m = changeIndices.size(); + int l = 1, r = m + 1; + while (l < r) { + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; + } +}; \ No newline at end of file diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.go b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.go new file mode 100644 index 0000000000000..7a39ba88aa619 --- /dev/null +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.go @@ -0,0 +1,26 @@ +func earliestSecondToMarkIndices(nums []int, changeIndices []int) int { + n, m := len(nums), len(changeIndices) + l := sort.Search(m+1, func(t int) bool { + last := make([]int, n+1) + for s, i := range changeIndices[:t] { + last[i] = s + } + decrement, marked := 0, 0 + for s, i := range changeIndices[:t] { + if last[i] == s { + if decrement < nums[i-1] { + return false + } + decrement -= nums[i-1] + marked++ + } else { + decrement++ + } + } + return marked == n + }) + if l > m { + return -1 + } + return l +} \ No newline at end of file diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.java b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.java index 0c664fd14fb28..c8e658bec3501 100644 --- a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.java +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.java @@ -1,45 +1,42 @@ class Solution { + private int[] nums; + private int[] changeIndices; + public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) { - int l = 0; - int r = changeIndices.length + 1; + this.nums = nums; + this.changeIndices = changeIndices; + int m = changeIndices.length; + int l = 1, r = m + 1; while (l < r) { - final int m = (l + r) / 2; - if (canMark(nums, changeIndices, m)) { - r = m; + int mid = (l + r) >> 1; + if (check(mid)) { + r = mid; } else { - l = m + 1; + l = mid + 1; } } - return l <= changeIndices.length ? l : -1; + return l > m ? -1 : l; } - private boolean canMark(int[] nums, int[] changeIndices, int second) { - int numMarked = 0; - int decrement = 0; - // indexToLastSecond[i] := the last second to mark the index i - int[] indexToLastSecond = new int[nums.length]; - Arrays.fill(indexToLastSecond, -1); - - for (int i = 0; i < second; ++i) { - indexToLastSecond[changeIndices[i] - 1] = i; + private boolean check(int t) { + int[] last = new int[nums.length + 1]; + for (int s = 0; s < t; ++s) { + last[changeIndices[s]] = s; } - - for (int i = 0; i < second; ++i) { - // Convert to 0-indexed. - final int index = changeIndices[i] - 1; - if (i == indexToLastSecond[index]) { - // Reach the last occurrence of the number. - // So, the current second will be used to mark the index. - if (nums[index] > decrement) { - // The decrement is less than the number to be marked. + int decrement = 0; + int marked = 0; + for (int s = 0; s < t; ++s) { + int i = changeIndices[s]; + if (last[i] == s) { + if (decrement < nums[i - 1]) { return false; } - decrement -= nums[index]; - ++numMarked; + decrement -= nums[i - 1]; + ++marked; } else { ++decrement; } } - return numMarked == nums.length; + return marked == nums.length; } -} +} \ No newline at end of file diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.py b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.py new file mode 100644 index 0000000000000..96a7a96a195af --- /dev/null +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.py @@ -0,0 +1,21 @@ +class Solution: + def earliestSecondToMarkIndices( + self, nums: List[int], changeIndices: List[int] + ) -> int: + def check(t: int) -> bool: + decrement = 0 + marked = 0 + last = {i: s for s, i in enumerate(changeIndices[:t])} + for s, i in enumerate(changeIndices[:t]): + if last[i] == s: + if decrement < nums[i - 1]: + return False + decrement -= nums[i - 1] + marked += 1 + else: + decrement += 1 + return marked == len(nums) + + m = len(changeIndices) + l = bisect_left(range(1, m + 2), True, key=check) + 1 + return -1 if l > m else l diff --git a/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.ts b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.ts new file mode 100644 index 0000000000000..4d9c7742d1216 --- /dev/null +++ b/solution/3000-3099/3048.Earliest Second to Mark Indices I/Solution.ts @@ -0,0 +1,33 @@ +function earliestSecondToMarkIndices(nums: number[], changeIndices: number[]): number { + const [n, m] = [nums.length, changeIndices.length]; + let [l, r] = [1, m + 1]; + const check = (t: number): boolean => { + const last: number[] = Array(n + 1).fill(0); + for (let s = 0; s < t; ++s) { + last[changeIndices[s]] = s; + } + let [decrement, marked] = [0, 0]; + for (let s = 0; s < t; ++s) { + const i = changeIndices[s]; + if (last[i] === s) { + if (decrement < nums[i - 1]) { + return false; + } + decrement -= nums[i - 1]; + ++marked; + } else { + ++decrement; + } + } + return marked === n; + }; + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l > m ? -1 : l; +}