Kimi LeetCode 3395. 唯一中间众数子序列 I Rust实现
以下是 LeetCode 3395. 唯一中间众数子序列 I 的 Rust 实现:
```rust
use std::collections::HashMap;
impl Solution {
pub fn subsequences_with_middle_mode(nums: Vec<i32>) -> i32 {
const MOD: i64 = 1_000_000_007;
let n = nums.len();
// 总方案数 = C(n, 5)
let mut ans: i64 = (n as i64) * ((n - 1) as i64) * ((n - 2) as i64)
* ((n - 3) as i64) * ((n - 4) as i64) / 120;
// 后缀频率:每个数字在后缀中的出现次数
let mut suf: HashMap<i32, i32> = HashMap::new();
for &num in &nums {
*suf.entry(num).or_insert(0) += 1;
}
// 前缀频率:每个数字在前缀中的出现次数(初始为0)
let mut pre: HashMap<i32, i32> = suf.keys().map(|&k| (k, 0)).collect();
// cp = sum of C(pre[y], 2) for all y
let mut cp: i64 = 0;
// cs = sum of C(suf[y], 2) for all y
let mut cs: i64 = suf.values().map(|&c| (c as i64) * ((c - 1) as i64) / 2).sum();
// ps = sum of pre[y] * suf[y] for all y
let mut ps: i64 = 0;
// p2s = sum of pre[y]^2 * suf[y] for all y
let mut p2s: i64 = 0;
// ps2 = sum of pre[y] * suf[y]^2 for all y
let mut ps2: i64 = 0;
// 枚举中间元素 x(位置 i 作为子序列的第3个元素)
for i in 0..n - 2 {
let x = nums[i];
let left = i as i64; // x 左侧的元素个数
let right = (n - 1 - i) as i64; // x 右侧的元素个数
// 将 x 从后缀移到"当前处理"
*suf.get_mut(&x).unwrap() -= 1;
let sx = suf[&x] as i64; // x 在后缀中的出现次数
let px = pre[&x] as i64; // x 在前缀中的出现次数
// 更新后缀相关统计量(去掉 x 的旧贡献)
cs -= sx; // C(sx+1, 2) - C(sx, 2) = sx
ps -= px; // pre[x] * suf[x] 的变化
p2s -= px * px; // pre[x]^2 * suf[x] 的变化
ps2 -= (2 * sx + 1) * px; // pre[x] * suf[x]^2 的变化
// 减去不满足条件的子序列数(正难则反)
// 情况1: x 只出现1次(仅作为中间元素)
// 左右各选2个非x元素
ans -= (left - px) * (left - px - 1) / 2
* (right - sx) * (right - sx - 1) / 2;
// 情况2: x 出现2次,某个 y 也出现2次(y 的两次都在左侧)
ans -= (cp - px * (px - 1) / 2) * sx * (right - sx);
// 情况3: x 出现2次,某个 y 也出现2次(y 的两次都在右侧)
ans -= (cs - sx * (sx - 1) / 2) * px * (left - px);
// 情况4: x 出现2次,某个 y 也出现2次(y 左右各一次,x 来自左侧)
ans -= ((ps - px * sx) * (right - sx)
- (ps2 - px * sx * sx)) * px;
// 情况5: x 出现2次,某个 y 也出现2次(y 左右各一次,x 来自右侧)
ans -= ((ps - px * sx) * (left - px)
- (p2s - px * px * sx)) * sx;
// 更新前缀相关统计量(加上 x 的新贡献)
*pre.get_mut(&x).unwrap() += 1;
cp += px; // C(px+1, 2) - C(px, 2) = px
ps += sx; // pre[x] * suf[x] 的变化
ps2 += sx * sx; // pre[x] * suf[x]^2 的变化
p2s += (2 * px + 1) * sx; // pre[x]^2 * suf[x] 的变化
}
((ans % MOD + MOD) % MOD) as i32
}
}
```
思路说明
核心思想:正难则反(容斥原理)
总方案数 = 所有长度为 5 的子序列数 `C(n, 5)`,减去不满足"唯一中间众数"条件的子序列数。
变量定义(前缀-后缀统计量):
变量 含义
`cp` `Σ C(pre[y], 2)` — 前缀中所有数字的两两组合数之和
`cs` `Σ C(suf[y], 2)` — 后缀中所有数字的两两组合数之和
`ps` `Σ pre[y] × suf[y]` — 前缀后缀配对数之和
`p2s` `Σ pre[y]² × suf[y]`
`ps2` `Σ pre[y] × suf[y]²`
五种不满足条件的情况(`x` 为中间元素):
情况 描述 计数公式
1 `x` 只出现1次,其余4个元素任意 `C(L-px, 2) × C(R-sx, 2)`
2 `x` 出现2次,某 `y` 也出现2次(`y` 的两次都在左侧) `(cp - C(px,2)) × sx × (R-sx)`
3 `x` 出现2次,某 `y` 也出现2次(`y` 的两次都在右侧) `(cs - C(sx,2)) × px × (L-px)`
4 `x` 出现2次,某 `y` 也出现2次(`y` 左右各一次,`x` 来自左侧) `((ps-px×sx)(R-sx) - (ps2-px×sx²)) × px`
5 `x` 出现2次,某 `y` 也出现2次(`y` 左右各一次,`x` 来自右侧) `((ps-px×sx)(L-px) - (p2s-px²×sx)) × sx`
复杂度:时间 O(n),空间 O(u)(u 为不同数字个数,u ≤ n ≤ 1000)。
更多推荐



所有评论(0)