|
82 | 82 |
|
83 | 83 | ## 解法
|
84 | 84 |
|
85 |
| -### 方法一 |
| 85 | +### 方法一:二分查找 |
| 86 | + |
| 87 | +我们注意到,如果我们能够在 $t$ 秒内标记所有下标,那么我们也能在 $t' \geq t$ 秒内标记所有下标。因此,我们可以使用二分查找的方法找到最早的秒数。 |
| 88 | + |
| 89 | +我们定义二分查找的左右边界分别为 $l = 1$ 和 $r = m + 1$,其中 $m$ 是数组 `changeIndices` 的长度。对于每一个 $t = \frac{l + r}{2}$,我们检查是否能在 $t$ 秒内标记所有下标。如果能,我们将右边界移动到 $t$,否则我们将左边界移动到 $t + 1$。最终,我们判定左边界是否大于 $m$,如果是则返回 $-1$,否则返回左边界。 |
| 90 | + |
| 91 | +题目的关键在于如何判断是否能在 $t$ 秒内标记所有下标。我们可以使用一个数组 $last$ 记录每一个下标最晚需要被标记的时间,用一个变量 $decrement$ 记录当前可以减少的次数,用一个变量 $marked$ 记录已经被标记的下标的数量。 |
| 92 | + |
| 93 | +我们遍历数组 `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`。 |
| 94 | + |
| 95 | +时间复杂度 $O(m \times \log m)$,空间复杂度 $O(n)$。其中 $n$ 和 $m$ 分别是数组 `nums` 和 `changeIndices` 的长度。 |
86 | 96 |
|
87 | 97 | <!-- tabs:start -->
|
88 | 98 |
|
89 | 99 | ```python
|
90 |
| - |
| 100 | +class Solution: |
| 101 | + def earliestSecondToMarkIndices( |
| 102 | + self, nums: List[int], changeIndices: List[int] |
| 103 | + ) -> int: |
| 104 | + def check(t: int) -> bool: |
| 105 | + decrement = 0 |
| 106 | + marked = 0 |
| 107 | + last = {i: s for s, i in enumerate(changeIndices[:t])} |
| 108 | + for s, i in enumerate(changeIndices[:t]): |
| 109 | + if last[i] == s: |
| 110 | + if decrement < nums[i - 1]: |
| 111 | + return False |
| 112 | + decrement -= nums[i - 1] |
| 113 | + marked += 1 |
| 114 | + else: |
| 115 | + decrement += 1 |
| 116 | + return marked == len(nums) |
| 117 | + |
| 118 | + m = len(changeIndices) |
| 119 | + l = bisect_left(range(1, m + 2), True, key=check) + 1 |
| 120 | + return -1 if l > m else l |
91 | 121 | ```
|
92 | 122 |
|
93 | 123 | ```java
|
94 | 124 | class Solution {
|
| 125 | + private int[] nums; |
| 126 | + private int[] changeIndices; |
| 127 | + |
95 | 128 | public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) {
|
96 |
| - int l = 0; |
97 |
| - int r = changeIndices.length + 1; |
| 129 | + this.nums = nums; |
| 130 | + this.changeIndices = changeIndices; |
| 131 | + int m = changeIndices.length; |
| 132 | + int l = 1, r = m + 1; |
98 | 133 | while (l < r) {
|
99 |
| - final int m = (l + r) / 2; |
100 |
| - if (canMark(nums, changeIndices, m)) { |
101 |
| - r = m; |
| 134 | + int mid = (l + r) >> 1; |
| 135 | + if (check(mid)) { |
| 136 | + r = mid; |
102 | 137 | } else {
|
103 |
| - l = m + 1; |
| 138 | + l = mid + 1; |
104 | 139 | }
|
105 | 140 | }
|
106 |
| - return l <= changeIndices.length ? l : -1; |
| 141 | + return l > m ? -1 : l; |
107 | 142 | }
|
108 | 143 |
|
109 |
| - private boolean canMark(int[] nums, int[] changeIndices, int second) { |
110 |
| - int numMarked = 0; |
111 |
| - int decrement = 0; |
112 |
| - // indexToLastSecond[i] := the last second to mark the index i |
113 |
| - int[] indexToLastSecond = new int[nums.length]; |
114 |
| - Arrays.fill(indexToLastSecond, -1); |
115 |
| - |
116 |
| - for (int i = 0; i < second; ++i) { |
117 |
| - indexToLastSecond[changeIndices[i] - 1] = i; |
| 144 | + private boolean check(int t) { |
| 145 | + int[] last = new int[nums.length + 1]; |
| 146 | + for (int s = 0; s < t; ++s) { |
| 147 | + last[changeIndices[s]] = s; |
118 | 148 | }
|
119 |
| - |
120 |
| - for (int i = 0; i < second; ++i) { |
121 |
| - // Convert to 0-indexed. |
122 |
| - final int index = changeIndices[i] - 1; |
123 |
| - if (i == indexToLastSecond[index]) { |
124 |
| - // Reach the last occurrence of the number. |
125 |
| - // So, the current second will be used to mark the index. |
126 |
| - if (nums[index] > decrement) { |
127 |
| - // The decrement is less than the number to be marked. |
| 149 | + int decrement = 0; |
| 150 | + int marked = 0; |
| 151 | + for (int s = 0; s < t; ++s) { |
| 152 | + int i = changeIndices[s]; |
| 153 | + if (last[i] == s) { |
| 154 | + if (decrement < nums[i - 1]) { |
128 | 155 | return false;
|
129 | 156 | }
|
130 |
| - decrement -= nums[index]; |
131 |
| - ++numMarked; |
| 157 | + decrement -= nums[i - 1]; |
| 158 | + ++marked; |
132 | 159 | } else {
|
133 | 160 | ++decrement;
|
134 | 161 | }
|
135 | 162 | }
|
136 |
| - return numMarked == nums.length; |
| 163 | + return marked == nums.length; |
137 | 164 | }
|
138 | 165 | }
|
139 | 166 | ```
|
140 | 167 |
|
141 | 168 | ```cpp
|
| 169 | +class Solution { |
| 170 | +public: |
| 171 | + int earliestSecondToMarkIndices(vector<int>& nums, vector<int>& changeIndices) { |
| 172 | + int n = nums.size(); |
| 173 | + int last[n + 1]; |
| 174 | + auto check = [&](int t) { |
| 175 | + memset(last, 0, sizeof(last)); |
| 176 | + for (int s = 0; s < t; ++s) { |
| 177 | + last[changeIndices[s]] = s; |
| 178 | + } |
| 179 | + int decrement = 0, marked = 0; |
| 180 | + for (int s = 0; s < t; ++s) { |
| 181 | + int i = changeIndices[s]; |
| 182 | + if (last[i] == s) { |
| 183 | + if (decrement < nums[i - 1]) { |
| 184 | + return false; |
| 185 | + } |
| 186 | + decrement -= nums[i - 1]; |
| 187 | + ++marked; |
| 188 | + } else { |
| 189 | + ++decrement; |
| 190 | + } |
| 191 | + } |
| 192 | + return marked == n; |
| 193 | + }; |
142 | 194 |
|
| 195 | + int m = changeIndices.size(); |
| 196 | + int l = 1, r = m + 1; |
| 197 | + while (l < r) { |
| 198 | + int mid = (l + r) >> 1; |
| 199 | + if (check(mid)) { |
| 200 | + r = mid; |
| 201 | + } else { |
| 202 | + l = mid + 1; |
| 203 | + } |
| 204 | + } |
| 205 | + return l > m ? -1 : l; |
| 206 | + } |
| 207 | +}; |
143 | 208 | ```
|
144 | 209 |
|
145 | 210 | ```go
|
| 211 | +func earliestSecondToMarkIndices(nums []int, changeIndices []int) int { |
| 212 | + n, m := len(nums), len(changeIndices) |
| 213 | + l := sort.Search(m+1, func(t int) bool { |
| 214 | + last := make([]int, n+1) |
| 215 | + for s, i := range changeIndices[:t] { |
| 216 | + last[i] = s |
| 217 | + } |
| 218 | + decrement, marked := 0, 0 |
| 219 | + for s, i := range changeIndices[:t] { |
| 220 | + if last[i] == s { |
| 221 | + if decrement < nums[i-1] { |
| 222 | + return false |
| 223 | + } |
| 224 | + decrement -= nums[i-1] |
| 225 | + marked++ |
| 226 | + } else { |
| 227 | + decrement++ |
| 228 | + } |
| 229 | + } |
| 230 | + return marked == n |
| 231 | + }) |
| 232 | + if l > m { |
| 233 | + return -1 |
| 234 | + } |
| 235 | + return l |
| 236 | +} |
| 237 | +``` |
146 | 238 |
|
| 239 | +```ts |
| 240 | +function earliestSecondToMarkIndices(nums: number[], changeIndices: number[]): number { |
| 241 | + const [n, m] = [nums.length, changeIndices.length]; |
| 242 | + let [l, r] = [1, m + 1]; |
| 243 | + const check = (t: number): boolean => { |
| 244 | + const last: number[] = Array(n + 1).fill(0); |
| 245 | + for (let s = 0; s < t; ++s) { |
| 246 | + last[changeIndices[s]] = s; |
| 247 | + } |
| 248 | + let [decrement, marked] = [0, 0]; |
| 249 | + for (let s = 0; s < t; ++s) { |
| 250 | + const i = changeIndices[s]; |
| 251 | + if (last[i] === s) { |
| 252 | + if (decrement < nums[i - 1]) { |
| 253 | + return false; |
| 254 | + } |
| 255 | + decrement -= nums[i - 1]; |
| 256 | + ++marked; |
| 257 | + } else { |
| 258 | + ++decrement; |
| 259 | + } |
| 260 | + } |
| 261 | + return marked === n; |
| 262 | + }; |
| 263 | + while (l < r) { |
| 264 | + const mid = (l + r) >> 1; |
| 265 | + if (check(mid)) { |
| 266 | + r = mid; |
| 267 | + } else { |
| 268 | + l = mid + 1; |
| 269 | + } |
| 270 | + } |
| 271 | + return l > m ? -1 : l; |
| 272 | +} |
147 | 273 | ```
|
148 | 274 |
|
149 | 275 | <!-- tabs:end -->
|
|
0 commit comments