Skip to content

Commit 5827b3b

Browse files
committed
1 parent dd60b5d commit 5827b3b

File tree

2 files changed

+363
-2
lines changed

2 files changed

+363
-2
lines changed

src/array/ext/math.rs

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,213 @@ pub fn fizz_buzz(n: i32) -> Vec<String> {
177177
///
178178
/// 其中右移总共 `31 - (leading_zero)` (有符号数, 第一位为符号位, 忽略)
179179
/// 减1 次数, 即为1的个数
180-
///
180+
///
181181
/// 注意: 如果原本为0, 这时 `31 - (leading_zero)`可能有溢出
182182
///
183183
pub fn number_of_steps(num: i32) -> i32 {
184184
(num.count_ones() + 31u32.checked_sub(num.leading_zeros()).unwrap_or(0)) as i32
185185
}
186186

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+
187332
#[cfg(test)]
188333
mod tests {
189334
use super::*;
190335

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+
191387
#[test]
192388
fn test_number_of_steps() {
193389
struct TestCase {
@@ -202,7 +398,7 @@ mod tests {
202398
num: 123,
203399
expect: 12,
204400
},
205-
TestCase{num: 0, expect: 0}
401+
TestCase { num: 0, expect: 0 },
206402
]
207403
.into_iter()
208404
.enumerate()

src/sstr/mod.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,174 @@ pub fn reorder_spaces(text: String) -> String {
135135
unsafe { String::from_utf8_unchecked(result) }
136136
}
137137

138+
/// [830. 较大分组的位置](https://leetcode.cn/problems/positions-of-large-groups/)
139+
/// ```
140+
/// pub fn large_group_positions(s: String) -> Vec<Vec<i32>> {
141+
/// let s = s.as_bytes();
142+
/// if s.len() < 3 {
143+
/// return vec![];
144+
/// }
145+
///
146+
/// let mut ret: Vec<Vec<i32>> = vec![];
147+
///
148+
/// let (mut start, mut cursor) = (0, 1);
149+
/// let mut curr = s[0];
150+
///
151+
/// while cursor < s.len() {
152+
/// if s[cursor] == curr {
153+
/// if cursor - start + 1 >= 3 {
154+
/// match ret.last_mut() {
155+
/// Some(last) if last[0] == start as i32 => {
156+
/// last[1] = cursor as i32;
157+
/// cursor += 1;
158+
/// continue;
159+
/// }
160+
/// _ => {}
161+
/// }
162+
/// ret.push(vec![start as i32, cursor as i32]);
163+
/// }
164+
/// } else {
165+
/// start = cursor;
166+
/// curr = s[cursor];
167+
/// }
168+
/// cursor += 1;
169+
/// }
170+
///
171+
/// ret
172+
/// }
173+
/// ```
174+
pub fn large_group_positions(s: String) -> Vec<Vec<i32>> {
175+
let s = s.as_bytes();
176+
let mut ret: Vec<Vec<i32>> = vec![];
177+
let mut cnt = 1;
178+
for i in 0..s.len() {
179+
if i == s.len() - 1 || s[i] != s[i + 1] {
180+
if cnt >= 3 {
181+
// 必须是 `i + 1 - cnt`, 不能是 `i - cnt + 1`, 会溢出
182+
ret.push(vec![(i + 1 - cnt) as i32, i as i32]);
183+
}
184+
cnt = 1;
185+
} else {
186+
cnt += 1;
187+
}
188+
}
189+
ret
190+
}
191+
192+
/// [831. 隐藏个人信息](https://leetcode.cn/problems/masking-personal-information/)
193+
pub fn mask_pii(s: String) -> String {
194+
let (mut is_email, mut at_pos) = (false, 0);
195+
196+
let mut chrs = vec![];
197+
for &(mut b) in s.as_bytes() {
198+
if b == b'+' || b == b'-' || b == b'(' || b == b')' || b == b' ' {
199+
continue;
200+
}
201+
if b == b'@' {
202+
is_email = true;
203+
at_pos = chrs.len();
204+
} else if b >= b'A' && b <= b'Z' {
205+
b = b - b'A' + b'a';
206+
}
207+
chrs.push(b);
208+
}
209+
210+
let mut ret = vec![];
211+
if is_email {
212+
ret.push(chrs[0]);
213+
ret.extend_from_slice("*****".as_bytes());
214+
ret.extend_from_slice(&chrs[at_pos - 1..]);
215+
} else {
216+
if chrs.len() == 13 {
217+
ret.extend_from_slice("+***-***-***-".as_bytes());
218+
} else if chrs.len() == 12 {
219+
ret.extend_from_slice("+**-***-***-".as_bytes());
220+
} else if chrs.len() == 11 {
221+
ret.extend_from_slice("+*-***-***-".as_bytes());
222+
} else {
223+
ret.extend_from_slice("***-***-".as_bytes());
224+
}
225+
ret.extend_from_slice(&chrs[chrs.len() - 4..]);
226+
}
227+
String::from_utf8(ret).unwrap()
228+
}
229+
138230
#[cfg(test)]
139231
mod tests {
140232
use super::*;
233+
use crate::vec2;
234+
235+
#[test]
236+
fn test_mask_pii() {
237+
struct TestCase {
238+
s: &'static str,
239+
expect: &'static str,
240+
}
241+
242+
vec![
243+
TestCase {
244+
s: "LeetCode@LeetCode.com",
245+
expect: "l*****e@leetcode.com",
246+
},
247+
TestCase {
248+
s: "AB@qq.com",
249+
expect: "a*****b@qq.com",
250+
},
251+
TestCase {
252+
s: "1(234)567-890",
253+
expect: "***-***-7890",
254+
},
255+
TestCase {
256+
s: "86-(10)12345678",
257+
expect: "+**-***-***-5678",
258+
},
259+
TestCase{
260+
s: "+86(88)1513-7-74",
261+
expect: "+*-***-***-3774"
262+
},
263+
]
264+
.into_iter()
265+
.enumerate()
266+
.for_each(|(idx, testcase)| {
267+
let TestCase { s, expect } = testcase;
268+
let actual = mask_pii(s.to_string());
269+
assert_eq!(expect, actual, "case {} failed", idx);
270+
});
271+
}
272+
273+
#[test]
274+
fn test_large_group_positions() {
275+
struct TestCase {
276+
s: &'static str,
277+
expect: Vec<Vec<i32>>,
278+
}
279+
280+
vec![
281+
// TestCase {
282+
// s: "abbxxxxzzy",
283+
// expect: vec2![[3, 6]],
284+
// },
285+
// TestCase {
286+
// s: "abc",
287+
// expect: vec2![],
288+
// },
289+
// TestCase {
290+
// s: "abcdddeeeeaabbbcd",
291+
// expect: vec2![[3,5],[6,9],[12,14]],
292+
// },
293+
TestCase {
294+
s: "aaa",
295+
expect: vec2![[0, 2]],
296+
},
297+
]
298+
.into_iter()
299+
.enumerate()
300+
.for_each(|(idx, testcase)| {
301+
let TestCase { s, expect } = testcase;
302+
let actual = large_group_positions(s.to_string());
303+
assert_eq!(expect, actual, "case {} failed", idx);
304+
});
305+
}
141306

142307
#[test]
143308
fn test_reorder_spaces() {

0 commit comments

Comments
 (0)