@@ -177,17 +177,213 @@ pub fn fizz_buzz(n: i32) -> Vec<String> {
177
177
///
178
178
/// 其中右移总共 `31 - (leading_zero)` (有符号数, 第一位为符号位, 忽略)
179
179
/// 减1 次数, 即为1的个数
180
- ///
180
+ ///
181
181
/// 注意: 如果原本为0, 这时 `31 - (leading_zero)`可能有溢出
182
182
///
183
183
pub fn number_of_steps ( num : i32 ) -> i32 {
184
184
( num. count_ones ( ) + 31u32 . checked_sub ( num. leading_zeros ( ) ) . unwrap_or ( 0 ) ) as i32
185
185
}
186
186
187
+ /// [829. 连续整数求和](https://leetcode.cn/problems/consecutive-numbers-sum/)
188
+ ///
189
+ /// 根据[等差数列的公式](https://en.wikipedia.org/wiki/Arithmetic_progression)
190
+ ///
191
+ /// ```
192
+ /// pub fn consecutive_numbers_sum(n: i32) -> i32 {
193
+ /// fn is_k_consecutive(n: i32, k: i32) -> bool {
194
+ /// if k % 2 == 1 {
195
+ /// return n % k == 0;
196
+ /// }
197
+ /// return n % k > 0 && 2 * n % k == 0;
198
+ /// }
199
+ /// let mut ans = 0;
200
+ /// let mut k = 1;
201
+ /// while k * (k + 1) <= n * 2 {
202
+ /// if is_k_consecutive(n, k) {
203
+ /// ans += 1;
204
+ /// }
205
+ /// k += 1
206
+ /// }
207
+ /// return ans;
208
+ /// }
209
+ /// ```
210
+ /// 根据[等差数列的公式](https://en.wikipedia.org/wiki/Arithmetic_progression), 可以得到以下推导,
211
+ /// 假定起始项为$a$, 差$d$为1 项数$k$, 和为$n$, 则
212
+ /// $$
213
+ /// n = \frac{k \times (a+a+(k-1))}{2}
214
+ /// $$
215
+ ///
216
+ /// 等式变化可以得到
217
+ ///
218
+ /// $n = \frac{k \times (a+a+(k-1))}{2} \Rightarrow 2n = k(2a+(k-1)) \Rightarrow \frac{2n}{k} = 2a + k - 1$
219
+ ///
220
+ /// 由于题目要求起始项需要为*正整数*, 也就是$2a + k - 1$为正整数, 因此 `2 * n % k == 0`
221
+ ///
222
+ /// 同时有$2a \ge 2$, 所以 $2a + k - 1 \ge k + 1 \Rightarrow \frac{2n}{k} \ge k + 1$
223
+ ///
224
+ /// 综上, $k$为$2n$的约数, 且是较小的那个.
225
+ ///
226
+ /// 因此在$[1, \sqrt{2n}]$的范围内找$k$即可.
227
+ pub fn consecutive_numbers_sum ( n : i32 ) -> i32 {
228
+ let mut ans = 0 ;
229
+ let n = 2 * n;
230
+ let mut k = 1 ;
231
+
232
+ while k * k < n {
233
+ if n % k != 0 {
234
+ k += 1 ;
235
+ continue ;
236
+ }
237
+ if ( n / k - ( k - 1 ) ) % 2 == 0 {
238
+ ans += 1 ;
239
+ }
240
+ k += 1 ;
241
+ }
242
+ ans
243
+ }
244
+
245
+ /// [828. 统计子串中的唯一字符](https://leetcode.cn/problems/count-unique-characters-of-all-substrings-of-a-given-string/)
246
+ ///
247
+ /// 按照题意, 很容易写入如下枚举的方式, 时间复杂度 $O(n^3 + c \times n^2)$
248
+ /// 枚举边界 $n^2$, 内层统计个数 $n + C$
249
+ /// 最后超时
250
+ /// ```
251
+ /// pub fn unique_letter_string(s: String) -> i32 {
252
+ /// fn count_unique_chars(s: &str) -> usize {
253
+ /// use std::collections::HashMap;
254
+ /// let mut counter = HashMap::new();
255
+ /// for chr in s.chars() {
256
+ /// *counter.entry(chr).or_insert(0) += 1;
257
+ /// }
258
+ /// counter.into_iter().filter(|(_, v)| *v == 1).count()
259
+ /// }
260
+ /// let mut cnt = 0;
261
+ /// for i in 0..s.len() {
262
+ /// for j in i..s.len() {
263
+ /// cnt += count_unique_chars(s.get(i..=j).unwrap());
264
+ /// }
265
+ /// }
266
+ /// cnt as i32
267
+ /// }
268
+ /// ```
269
+ ///
270
+ /// 优化方向: 将内层的遍历换成类似"滑动窗口", 不做重复统计
271
+ /// ```
272
+ /// pub fn unique_letter_string(s: String) -> i32 {
273
+ /// use std::collections::HashMap;
274
+ ///
275
+ /// let s = s.as_bytes();
276
+ ///
277
+ /// let mut cnt = 0;
278
+ /// for i in 0..s.len() {
279
+ /// let mut counter = HashMap::new();
280
+ /// for j in i..s.len() {
281
+ /// {
282
+ /// *counter.entry(s[j]).or_insert(0) += 1;
283
+ /// }
284
+ /// cnt += counter.iter().filter(|(_, &v)| v == 1).count();
285
+ /// }
286
+ /// }
287
+ /// cnt as i32
288
+ /// }
289
+ /// ```
290
+ /// 时间复杂度 $O(n^2 + c \times n^2)$, 仍然超时
291
+ ///
292
+ /// 其他思路:
293
+ /// 对于字串 `BCADEF`, 假定其前后都是`A`, 即`(A)BCADEF(A)`, 含统计字符`A`的唯一串有
294
+ /// `BCA, BCAD, BCADE, BCADEF, CA, CAD, CADE, CADEF, A, AD, ADE, ADEF`
295
+ /// 站在`BC`的角度看, 后面有4种选择, `(), D, DE, DEF`
296
+ /// 站在`DEF`的角度看, 前面有3种选择, `BC, C, ()`
297
+ ///
298
+ /// 所以这个段内, 含统计`A`字符的唯一串有 3 * 4 = 12, 即 (2+1) * (3+1),
299
+ /// 2为当前A到前一个A之间字符个数, 3为当前A到后一个A之间字符个数
300
+ ///
301
+ /// 基于此, 可以对任意一个`(A)..A..(A)`进行计算, 最终加和即为结果
302
+ ///
303
+ /// 边界情况: 字符只出现一次
304
+ ///
305
+ pub fn unique_letter_string ( s : String ) -> i32 {
306
+ let s = s. as_bytes ( ) ;
307
+
308
+ let mut last_pos = vec ! [ -1 ; 26 ] ;
309
+ let mut curr_pos = vec ! [ -1 ; 26 ] ;
310
+
311
+ let mut ans = 0 ;
312
+
313
+ for ( pos, & b) in s. iter ( ) . enumerate ( ) {
314
+ let i = ( b - b'A' ) as usize ;
315
+ let pos = pos as i32 ;
316
+ if curr_pos[ i] > -1 {
317
+ // 这个字符之前出现过, 这时的pos对应上述推导中的后一个
318
+ ans = ans + ( pos - curr_pos[ i] ) * ( curr_pos[ i] - last_pos[ i] ) ;
319
+ }
320
+ last_pos[ i] = curr_pos[ i] ;
321
+ curr_pos[ i] = pos;
322
+ }
323
+ // 对于只出现过一次的字符, 上面循环统计不到, 即等效 后一个 的位置为字符结尾
324
+ for ( last, curr) in last_pos. into_iter ( ) . zip ( curr_pos. into_iter ( ) ) {
325
+ if curr > -1 {
326
+ ans = ans + ( curr - last) * ( s. len ( ) as i32 - curr) ;
327
+ }
328
+ }
329
+ ans
330
+ }
331
+
187
332
#[ cfg( test) ]
188
333
mod tests {
189
334
use super :: * ;
190
335
336
+ #[ test]
337
+ fn test_unique_letter_string ( ) {
338
+ struct TestCase {
339
+ s : & ' static str ,
340
+ expect : i32 ,
341
+ }
342
+
343
+ vec ! [
344
+ TestCase {
345
+ s: "ABC" ,
346
+ expect: 10 ,
347
+ } ,
348
+ TestCase {
349
+ s: "ABA" ,
350
+ expect: 8 ,
351
+ } ,
352
+ TestCase {
353
+ s: "LEETCODE" ,
354
+ expect: 92 ,
355
+ } ,
356
+ ]
357
+ . into_iter ( )
358
+ . enumerate ( )
359
+ . for_each ( |( idx, testcase) | {
360
+ let TestCase { s, expect } = testcase;
361
+ let actual = unique_letter_string ( s. to_string ( ) ) ;
362
+ assert_eq ! ( expect, actual, "case {} failed" , idx) ;
363
+ } ) ;
364
+ }
365
+
366
+ #[ test]
367
+ fn test_consecutive_numbers_sum ( ) {
368
+ struct TestCase {
369
+ n : i32 ,
370
+ expect : i32 ,
371
+ }
372
+
373
+ vec ! [
374
+ TestCase { n: 5 , expect: 2 } ,
375
+ TestCase { n: 9 , expect: 3 } ,
376
+ TestCase { n: 15 , expect: 4 } ,
377
+ ]
378
+ . into_iter ( )
379
+ . enumerate ( )
380
+ . for_each ( |( idx, testcase) | {
381
+ let TestCase { n, expect } = testcase;
382
+ let actual = consecutive_numbers_sum ( n) ;
383
+ assert_eq ! ( expect, actual, "case {} failed" , idx) ;
384
+ } ) ;
385
+ }
386
+
191
387
#[ test]
192
388
fn test_number_of_steps ( ) {
193
389
struct TestCase {
@@ -202,7 +398,7 @@ mod tests {
202
398
num: 123 ,
203
399
expect: 12 ,
204
400
} ,
205
- TestCase { num: 0 , expect: 0 }
401
+ TestCase { num: 0 , expect: 0 } ,
206
402
]
207
403
. into_iter ( )
208
404
. enumerate ( )
0 commit comments