最小日期区间覆盖算法:带星期过滤的区间最小覆盖集合求解
解决按星期几过滤的日期区间最小化问题
嘿,你提到的这个问题我之前梳理过类似的思路——你最开始想到的按起始日期排序、合并连续区间的方向是对的,但忽略了星期几掩码这个核心变量的兼容性判断,这才导致思路卡壳。下面给你拆解具体的可落地步骤,帮你得到最小的等价区间集合:
第一步:先把所有输入区间标准化
别小看这一步,格式统一能避免后续很多坑:
- 把所有日期转成无歧义的格式,比如
YYYY-MM-DD(比如2001 APR 1转成2001-04-01) - 把星期几的数字掩码转成直观的星期几集合(比如
17是二进制10001,对应周日和周一,就转成{周日, 周一}),或者直接用二进制位运算的方式处理,方便后续对比合并 - 检查每个区间的起始日期是否≤结束日期,要是有写反的先修正过来
第二步:按起始日期给区间排序
把标准化后的区间按起始日期升序排列,如果起始日期相同,就按结束日期降序排(让大区间排在前面,后续合并的时候能优先处理覆盖范围大的,减少重复操作)
第三步:核心操作——迭代合并区间
维护一个结果列表,逐个处理排序后的区间:
- 如果结果列表是空的,直接把当前区间加进去就行
- 取出结果列表的最后一个区间
last,和当前待处理的区间curr对比,分情况处理:- ✅ 日期连续+掩码完全相同:比如
last是{2001-04-01至2001-04-15, 1}(4月前半月周日),curr是{2001-04-16至2001-04-30, 1}(4月后半月周日),直接合并成{2001-04-01至2001-04-30, 1} - ✅ 日期重叠/包含+掩码完全相同:比如
last是{2001-04-05至2001-04-25, 16}(4月中旬周一),curr是{2001-04-01至2001-04-30, 16}(4月全月周一),合并成覆盖两者的最大范围{2001-04-01至2001-04-30, 16} - ✅ 日期范围完全相同+掩码不同:比如
last是{2001-04-01至2001-04-30, 1},curr是{2001-04-01至2001-04-30, 16},直接把掩码按位或合并(1 | 16 = 17),得到{2001-04-01至2001-04-30, 17},这样就少了一个区间 - ❌ 日期连续/重叠但掩码无交集:比如
last是{2001-04-01至2001-04-15, 1},curr是{2001-04-16至2001-04-30, 16},这种情况不能合并,只能把curr加入结果列表
- ✅ 日期连续+掩码完全相同:比如
第四步:最后做一轮去重和优化
遍历结果列表,做两个收尾操作:
- 删除重复的区间:如果有两个区间的日期范围和掩码完全一样,直接删掉其中一个
- 删除被完全包含的区间:比如结果里同时有
{2001-04-01至2001-04-30, 17}和{2001-04-10至2001-04-20, 17},后者的所有日期都被前者覆盖,直接删掉后者就行
给你举个实际的例子,输入这4个区间:
{2001 APR 1 - 2001 APR 15, 1}{2001 APR 16 - 2001 APR 30, 1}{2001 APR 1 - 2001 APR 30, 16}{2001 MAY 1 - 2001 MAY 31, 17}
按照步骤处理后:
- 先合并1和2成
{2001-04-01至2001-04-30, 1} - 再和3合并成
{2001-04-01至2001-04-30, 17} - 检查和4的日期连续性(4月30日是周二,5月1日是周三,属于连续日期),且掩码都是17,所以合并成
{2001-04-01至2001-05-31, 17}
最终结果只剩1个区间,完美实现了最小化的目标。
需要提醒的几个坑:
- 一定要统一星期几掩码的定义(比如明确哪一位对应周日、周一,不然合并的时候会搞错)
- 判断日期连续性的时候要考虑跨月、跨年的情况,比如2001年12月31日到2002年1月1日是连续的
- 别忽略日期范围相同但掩码不同的情况,这种合并掩码的操作能大幅减少区间数量
内容的提问来源于stack exchange,提问作者Narek Margaryan
相关产品推荐
相关产品推荐

