预定义子集匹配:寻找被输入子集包含的子集的算法优化
子集包含查询优化方案
问题描述
给定全集 {1,2,...,N},以及n个预定义固定子集(示例:
N = 5, n = 3 s₁ = {1, 4} s₂ = {2, 4, 5} s₃ = {1, 5}
),允许提前进行任意预计算。需要处理任意输入子集(取自全集),返回所有被该输入子集包含的预定义子集;同时需支持简化任务:返回任意一个符合条件的预定义子集。
约束与参数
- 算法复杂度不能依赖子集数量n(仅可依赖结果规模)
- 参数预期:
N≈10^6,n≈10^5,预定义子集平均大小为10(常量),输入子集平均大小为30,结果规模通常为0-5(多为0或1)
原方案痛点
当前方案为每个数1~N预存包含它的子集列表,处理输入时遍历每个输入数对应的子集列表、统计匹配元素数。但如果存在高频元素(比如出现在所有子集的元素),每次处理该数都要遍历全部n个子集,效率极低。
优化解决方案
一、完整查询:返回所有符合条件的子集
预计算阶段
- 为每个预定义子集
s_i分配唯一ID,记录其元素总数size_i,并将子集元素存储为哈希集合(用于后续O(1)存在性检查)。 - 统计元素出现频率:用数组统计每个元素在所有预定义子集中的出现次数(N=1e6,数组仅占约4MB,完全可行)。
- 建立低频元素映射:对每个预定义子集
s_i,挑选其中出现频率最低的3个元素(可根据实际调整,核心是选关联子集最少的元素),为这3个元素分别建立映射:元素值 → 关联的子集ID列表。
查询阶段
- 将输入子集转换为哈希集合
input_set,方便快速判断元素是否存在。 - 从输入子集的元素中,筛选出存在于低频元素映射中的元素,选择其中关联子集数量最少的元素,取出对应的子集ID列表作为候选集。
- 遍历候选集中的每个子集ID:
- 检查该子集的所有元素是否都在
input_set中(预定义子集平均大小10,这步成本极低)。 - 若全部存在,则加入结果列表。
- 检查该子集的所有元素是否都在
- 返回结果列表。
复杂度说明
预计算阶段复杂度为O(n * 平均子集大小),完全符合规模要求;查询阶段仅处理极小的候选集(通常远小于n),整体复杂度仅依赖结果规模,与n无关。
二、简化查询:返回任意一个符合条件的子集
基于完整查询的思路做简化,进一步提升效率:
预计算阶段
- 同完整查询的步骤1、2。
- 对每个预定义子集
s_i,仅挑选出现频率最低的1个元素,建立映射:元素值 → 关联的子集ID列表。
查询阶段
- 将输入子集转换为哈希集合
input_set。 - 遍历输入子集的元素,找到第一个存在于映射中的元素,取出其关联的子集ID列表。
- 逐个检查列表中的子集是否被
input_set包含,找到第一个符合条件的子集后直接返回。
优势
因为结果规模多为0或1,这种方式通常只需检查1-2个子集就能得到结果,效率极高。
极端情况补充处理
如果某个预定义子集的所有元素都是高频元素(极端罕见),可将其ID存入单独的"高频子集池"。查询时仅当输入子集包含所有高频元素时,才遍历该池子(根据参数,这种情况几乎可以忽略)。
内容的提问来源于stack exchange,提问作者Tarapararamus
相关产品推荐
相关产品推荐

