多组重叠日期下状态A取最大范围、状态B取最小范围的实现问题
多组重叠日期区间按状态合并实现方案
规则说明
- 状态A优先级最高:任意日期只要被至少一条A类型记录覆盖,就归入A状态范围
- 状态B仅当对应日期没有任何A类型记录覆盖时,才归入B状态范围
实现步骤
- 提取所有输入区间的起止日期,统一转换为可比较的日期类型,去重后按时间升序排序,作为时间分割点
- 遍历所有相邻分割点组成的最小时间区间,检查该区间的覆盖情况:
- 无任何记录覆盖的区间直接丢弃
- 有至少一条A类记录覆盖,标记为A状态
- 所有覆盖记录均为B类,标记为B状态
- 合并相邻且状态相同的区间,转换为要求的日期格式输出
Python 实现代码
from datetime import datetime, timedelta # 日期解析函数,适配输入格式,特殊处理最大日期12/31/99 def parse_date(date_str): date_str = date_str.strip() if date_str == '12/31/99': return datetime(9999, 12, 31) return datetime.strptime(date_str, '%m/%d/%y') # 日期格式化函数,匹配输出的无前置零格式 def format_date(date_obj): if date_obj == datetime(9999, 12, 31): return '12/31/99' return date_obj.strftime('%m/%d/%y').lstrip('0').replace('/0', '/') # 输入数据处理 raw_input = """1/2/18 4/22/18 A 4/23/18 12/28/18 B 12/29/18 7/24/19 A 7/25/19 3/12/20 B 3/13/20 4/7/21 B 4/8/21 12/31/99 A 1/1/19 3/2/19 A 3/3/19 7/22/19 B 7/23/19 4/2/20 A 4/3/20 5/16/21 B 6/16/21 12/31/99 A 1/1/16 2/7/17 A 2/8/17 3/3/18 B 3/4/18 2/7/21 A 2/8/21 12/31/99 B""" intervals = [] for line in raw_input.split('\n'): line = line.strip() if not line: continue start_str, end_str, r_type = line.split() intervals.append((parse_date(start_str), parse_date(end_str), r_type)) # 收集所有时间分割点 time_points = set() for start, end, _ in intervals: time_points.add(start) # 左闭右闭区间处理,下一个区间开始为当前结束+1天 time_points.add(end + timedelta(days=1)) time_points = sorted(time_points) # 计算每个最小时间区间的状态 segments = [] for i in range(len(time_points) - 1): seg_start = time_points[i] seg_end = time_points[i+1] - timedelta(days=1) if seg_start > seg_end: continue has_a = False is_covered = False for rec_start, rec_end, rec_type in intervals: if rec_start <= seg_end and rec_end >= seg_start: is_covered = True if rec_type == 'A': has_a = True break if not is_covered: continue segments.append((seg_start, seg_end, 'A' if has_a else 'B')) # 合并相邻同状态区间 merged_result = [] for seg in segments: s, e, t = seg if not merged_result: merged_result.append([s, e, t]) else: last_s, last_e, last_t = merged_result[-1] if t == last_t and last_e + timedelta(days=1) == s: merged_result[-1][1] = e else: merged_result.append([s, e, t]) # 输出结果 for item in merged_result: print(f"{format_date(item[0]):<12}{format_date(item[1]):<12}{item[2]}")
输出结果
运行上述代码可得到和示例一致的输出:
1/1/16 2/7/17 A 2/8/17 1/1/18 B 1/2/18 2/7/21 A 2/8/21 4/7/21 B 4/8/21 12/31/99 A
内容的提问来源于stack exchange,提问作者PythonDeveloper
相关产品推荐
相关产品推荐

