Skip to content

Commit 0bfadf8

Browse files
authored
feat: add solutions to lc problem: No.3405 (#4503)
No.3405.Count the Number of Arrays with K Matching Adjacent Elements
1 parent 2c123b1 commit 0bfadf8

File tree

3 files changed

+212
-2
lines changed

3 files changed

+212
-2
lines changed

solution/3400-3499/3405.Count the Number of Arrays with K Matching Adjacent Elements/README.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,23 @@ tags:
9494

9595
<!-- solution:start -->
9696

97-
### 方法一
97+
### 方法一:组合数学 + 快速幂
98+
99+
长度为 $n$ 的数组,一共有 $n - 1$ 个相邻元素对。我们需要从这 $n - 1$ 个相邻元素对中选出 $k$ 个,使得这 $k$ 个相邻元素对的两个元素相等,那么剩下的 $n - 1 - k$ 个相邻元素对的两个元素不相等。
100+
101+
这相当于我们对数组进行 $n - 1 - k$ 次分割,得到 $n - k$ 个分段,每个分段子数组中的元素都相等,分割方案为 $C_{n - 1}^{n - 1 - k} = C_{n - 1}^{k}$。
102+
103+
第一段,我们可以任意选择一个元素,即在 $[1, m]$ 中选择一个元素,剩下的 $n - k - 1$ 个分段,只要确保每个分段的元素都不等于前一个分段的元素即可,因此剩下的每个分段都有 $m - 1$ 种选择,一共有 $m \times (m - 1)^{n - k - 1}$ 种选择。
104+
105+
将上述两部分结合起来,我们可以得到答案为:
106+
107+
$$
108+
C_{n - 1}^{k} \times m \times (m - 1)^{n - k - 1} \bmod (10^9 + 7)
109+
$$
110+
111+
在代码实现上,我们可以预处理阶乘和逆元,使用快速幂计算组合数。
112+
113+
忽略预处理的时间和空间,时间复杂度 $O(\log (n - k))$,空间复杂度 $O(1)$。
98114

99115
<!-- tabs:start -->
100116

@@ -278,6 +294,67 @@ export function countGoodArrays(n: number, m: number, k: number): number {
278294
}
279295
```
280296

297+
#### Rust
298+
299+
```rust
300+
impl Solution {
301+
pub fn count_good_arrays(n: i32, m: i32, k: i32) -> i32 {
302+
const N: usize = 1e5 as usize + 10;
303+
const MOD: i64 = 1_000_000_007;
304+
use std::sync::OnceLock;
305+
306+
static F: OnceLock<Vec<i64>> = OnceLock::new();
307+
static G: OnceLock<Vec<i64>> = OnceLock::new();
308+
309+
fn qpow(mut a: i64, mut k: i64, m: i64) -> i64 {
310+
let mut res = 1;
311+
while k != 0 {
312+
if k & 1 == 1 {
313+
res = res * a % m;
314+
}
315+
a = a * a % m;
316+
k >>= 1;
317+
}
318+
res
319+
}
320+
321+
fn init() -> (&'static Vec<i64>, &'static Vec<i64>) {
322+
F.get_or_init(|| {
323+
let mut f = vec![1i64; N];
324+
for i in 1..N {
325+
f[i] = f[i - 1] * i as i64 % MOD;
326+
}
327+
f
328+
});
329+
330+
G.get_or_init(|| {
331+
let f = F.get().unwrap();
332+
let mut g = vec![1i64; N];
333+
for i in 1..N {
334+
g[i] = qpow(f[i], MOD - 2, MOD);
335+
}
336+
g
337+
});
338+
339+
(F.get().unwrap(), G.get().unwrap())
340+
}
341+
342+
fn comb(f: &[i64], g: &[i64], m: usize, n: usize) -> i64 {
343+
f[m] * g[n] % MOD * g[m - n] % MOD
344+
}
345+
346+
let (f, g) = init();
347+
let n = n as usize;
348+
let m = m as i64;
349+
let k = k as usize;
350+
351+
let c = comb(f, g, n - 1, k);
352+
let pow = qpow(m - 1, (n - 1 - k) as i64, MOD);
353+
(c * m % MOD * pow % MOD) as i32
354+
}
355+
}
356+
```
357+
281358
<!-- tabs:end -->
282359

283360
<!-- solution:end -->

solution/3400-3499/3405.Count the Number of Arrays with K Matching Adjacent Elements/README_EN.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,23 @@ tags:
9090

9191
<!-- solution:start -->
9292

93-
### Solution 1
93+
### Solution 1: Combinatorics + Fast Power
94+
95+
For an array of length $n$, there are $n - 1$ pairs of adjacent elements. We need to select $k$ of these $n - 1$ adjacent pairs such that the two elements in each of these $k$ pairs are equal, and the remaining $n - 1 - k$ adjacent pairs have different elements.
96+
97+
This is equivalent to splitting the array $n - 1 - k$ times, resulting in $n - k$ segments, where all elements in each segment are equal. The number of ways to split is $C_{n - 1}^{n - 1 - k} = C_{n - 1}^{k}$.
98+
99+
For the first segment, we can choose any element from $[1, m]$. For the remaining $n - k - 1$ segments, we just need to ensure that the element in each segment is different from the previous segment, so each of these segments has $m - 1$ choices. In total, there are $m \times (m - 1)^{n - k - 1}$ ways to choose.
100+
101+
Combining the two parts above, we get the answer:
102+
103+
$$
104+
C_{n - 1}^{k} \times m \times (m - 1)^{n - k - 1} \bmod (10^9 + 7)
105+
$$
106+
107+
In the code implementation, we can precompute factorials and inverses, and use fast power to calculate combinations.
108+
109+
Ignoring the preprocessing time and space, the time complexity is $O(\log (n - k))$, and the space complexity is $O(1)$.
94110

95111
<!-- tabs:start -->
96112

@@ -274,6 +290,67 @@ export function countGoodArrays(n: number, m: number, k: number): number {
274290
}
275291
```
276292

293+
#### Rust
294+
295+
```rust
296+
impl Solution {
297+
pub fn count_good_arrays(n: i32, m: i32, k: i32) -> i32 {
298+
const N: usize = 1e5 as usize + 10;
299+
const MOD: i64 = 1_000_000_007;
300+
use std::sync::OnceLock;
301+
302+
static F: OnceLock<Vec<i64>> = OnceLock::new();
303+
static G: OnceLock<Vec<i64>> = OnceLock::new();
304+
305+
fn qpow(mut a: i64, mut k: i64, m: i64) -> i64 {
306+
let mut res = 1;
307+
while k != 0 {
308+
if k & 1 == 1 {
309+
res = res * a % m;
310+
}
311+
a = a * a % m;
312+
k >>= 1;
313+
}
314+
res
315+
}
316+
317+
fn init() -> (&'static Vec<i64>, &'static Vec<i64>) {
318+
F.get_or_init(|| {
319+
let mut f = vec![1i64; N];
320+
for i in 1..N {
321+
f[i] = f[i - 1] * i as i64 % MOD;
322+
}
323+
f
324+
});
325+
326+
G.get_or_init(|| {
327+
let f = F.get().unwrap();
328+
let mut g = vec![1i64; N];
329+
for i in 1..N {
330+
g[i] = qpow(f[i], MOD - 2, MOD);
331+
}
332+
g
333+
});
334+
335+
(F.get().unwrap(), G.get().unwrap())
336+
}
337+
338+
fn comb(f: &[i64], g: &[i64], m: usize, n: usize) -> i64 {
339+
f[m] * g[n] % MOD * g[m - n] % MOD
340+
}
341+
342+
let (f, g) = init();
343+
let n = n as usize;
344+
let m = m as i64;
345+
let k = k as usize;
346+
347+
let c = comb(f, g, n - 1, k);
348+
let pow = qpow(m - 1, (n - 1 - k) as i64, MOD);
349+
(c * m % MOD * pow % MOD) as i32
350+
}
351+
}
352+
```
353+
277354
<!-- tabs:end -->
278355

279356
<!-- solution:end -->
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
impl Solution {
2+
pub fn count_good_arrays(n: i32, m: i32, k: i32) -> i32 {
3+
const N: usize = 1e5 as usize + 10;
4+
const MOD: i64 = 1_000_000_007;
5+
use std::sync::OnceLock;
6+
7+
static F: OnceLock<Vec<i64>> = OnceLock::new();
8+
static G: OnceLock<Vec<i64>> = OnceLock::new();
9+
10+
fn qpow(mut a: i64, mut k: i64, m: i64) -> i64 {
11+
let mut res = 1;
12+
while k != 0 {
13+
if k & 1 == 1 {
14+
res = res * a % m;
15+
}
16+
a = a * a % m;
17+
k >>= 1;
18+
}
19+
res
20+
}
21+
22+
fn init() -> (&'static Vec<i64>, &'static Vec<i64>) {
23+
F.get_or_init(|| {
24+
let mut f = vec![1i64; N];
25+
for i in 1..N {
26+
f[i] = f[i - 1] * i as i64 % MOD;
27+
}
28+
f
29+
});
30+
31+
G.get_or_init(|| {
32+
let f = F.get().unwrap();
33+
let mut g = vec![1i64; N];
34+
for i in 1..N {
35+
g[i] = qpow(f[i], MOD - 2, MOD);
36+
}
37+
g
38+
});
39+
40+
(F.get().unwrap(), G.get().unwrap())
41+
}
42+
43+
fn comb(f: &[i64], g: &[i64], m: usize, n: usize) -> i64 {
44+
f[m] * g[n] % MOD * g[m - n] % MOD
45+
}
46+
47+
let (f, g) = init();
48+
let n = n as usize;
49+
let m = m as i64;
50+
let k = k as usize;
51+
52+
let c = comb(f, g, n - 1, k);
53+
let pow = qpow(m - 1, (n - 1 - k) as i64, MOD);
54+
(c * m % MOD * pow % MOD) as i32
55+
}
56+
}

0 commit comments

Comments
 (0)