频率统计与平方扩展 —— 对称子集构造
频率统计 + 平方扩展 —— 对称子集构造
日期: 2026-06-27
难度: Medium
标签: 哈希表、贪心、数学
题目链接: 3020. 子集中元素的最大数量
题目描述
给一个正整数数组,选一个子集使其元素能排成 [x, x², x⁴, ..., x^k, ..., x⁴, x², x] 的对称模式,求子集的最大元素数量。
第一次思路
第一眼看到提示说用 HashSet,但马上就发现不对——HashSet 去重之后丢掉了每个数出现几次的信息,而这个模式里除了中间元素出现 1 次,其他每个数都需要 2 次(左右对称各一个),所以必须用频率表。
另一个直觉是枚举所有可能的序列长度检查是否能构造,但显然不可行。
最终方案
真正的突破来自两个观察:
- 平方增长极快——1e9 以内最多平方 5 次就到顶,所以每个起点最多扩展几步就结束了,总复杂度很安全。
- 对称性决定了构造方式——从最小值
x向中间贪心扩展,每次消耗 2 个当前值,直到遇到只有 1 个的数作为中心,或到底后回退一步。
算法流程
- 统计频率表
freq - 数字 1 单独处理:1 的平方还是 1,所以全 1 序列长度就是 1 的个数(偶数则减 1,因为序列长度必须奇数)
- 对每个
x > 1且freq[x] ≥ 2:- 从
x出发,先消耗 2 个放在两端,len = 2 - 不断检查
next = cur²:- 存在且
freq[next] == 1→ 作中心,len += 1,结束 - 存在且
freq[next] ≥ 2→ 继续对称扩展,len += 2 - 不存在 → 无法形成对称序列,回退 1,将上一个数作中心
- 存在且
- 更新答案
- 从
关键细节
next > 1e9直接 break(数组里不可能有这个数)- 扩展到底后如果
len是偶数,说明缺中心,len--回退 - 1 和其他数字互斥,不能混在一个序列里
完整代码
第一版
先过了一遍逻辑,代码比较直白:单独循环统计 1,再用另一个 map 统计 > 1 的数,扩展部分用 mp.count() + mp[] 做了两次查找。
class Solution { |
优化版
几个优化点:
- 只用一个
freq表统计所有数,1 的处理也从同一个表里读,省去一次遍历 freq.find()代替count+[]组合,减少哈希表查找次数- 统一
int变量命名,逻辑更紧凑
class Solution { |
复杂度分析
| 复杂度 | 分析 |
|---|---|
| 时间复杂度 | O(n log C),C = 1e9,每个起点最多扩展约 5 次。实际遍历一次建表 + 对每个起点扩展几步 |
| 空间复杂度 | O(n),哈希表存储每个数的频率 |
心得总结
- 对称序列模式 → 非中心元素必须出现 2 次,中心元素 1 次 → 必须用频率表,不能只用 HashSet
- 平方增长极快 → 扩展深度有限,贪心直接构造即可,无需复杂 DP 或搜索
- 扩展到底没有中心元素时回退一步——这个细节容易漏,但规律就是奇数长度才有效
- 1 永远是特例,单独处理
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Chippandaの技术小站!
评论
