提莫攻击的中毒总时长如何合并重叠时间区间?
简化版
提莫攻击每次攻击产生一个中毒区间 [time, time+duration)。相邻攻击如果间隔小于 duration,中毒时间会重叠,新增时长只等于两次攻击间隔;否则新增完整 duration。累加每次新增贡献即可。
详细版
因为 timeSeries 已按升序排列,所以不需要真正构造所有区间。对于相邻两次攻击 time[i-1] 和 time[i],前一次中毒最多持续到 time[i-1]+duration。如果下一次攻击提前发生,重叠部分不能重复计算。
因此每次新增贡献是 min(duration, time[i]-time[i-1]),最后再加上最后一次攻击的完整 duration。时间复杂度 O(n),空间复杂度 O(1)。
完整版教学
一、每次攻击对应什么区间
攻击时间为 t,中毒持续 duration,通常表示半开区间:
[t, t + duration)
比如 t=1,duration=2,覆盖时间 [1,3),长度为 2。半开区间可以避免端点重复计算。
二、为什么重叠不能重复算
如果攻击发生在前一次中毒还没结束时,新中毒会刷新或延续效果,但重叠时间只能算一次。
攻击 1: [1,5)
攻击 3: [3,7)
合并后:[1,7),总长 6,不是 8
| 间隔 | 新增中毒时长 |
|---|---|
gap >= duration | 完整 duration |
gap < duration | 只新增 gap |
记忆钩子:相邻攻击贡献的是“前一次攻击到这次攻击之间真正新增的时间”。
三、为什么只看相邻攻击
timeSeries 已升序。当前攻击只可能和前一次中毒尾巴发生重叠;更早的攻击如果能影响当前,也一定已经通过前一次覆盖状态体现出来。
t0 <= t1 <= t2
计算 t2 时,只要看 t1 到 t2 的间隔
这让问题从区间合并简化成相邻差值累加。
四、公式怎么来
对第 i 次攻击之前的新增贡献:
gap = timeSeries[i] - timeSeries[i-1]
新增 = min(duration, gap)
最后一次攻击之后没有下一次攻击截断,所以要额外加完整 duration。
ans = sum(min(duration, gap_i)) + duration
如果只有一次攻击,答案就是 duration。
五、代码模板
int findPoisonedDuration(int[] timeSeries, int duration) {
if (timeSeries.length == 0) return 0;
int ans = 0;
for (int i = 1; i < timeSeries.length; i++) {
ans += Math.min(duration, timeSeries[i] - timeSeries[i - 1]);
}
return ans + duration;
}
这份写法不需要显式维护区间右端,因为相邻差值已经隐含了重叠长度。
六、用数字例子推演
timeSeries=[1,4], duration=2:
gap=3 >=2
答案 = 2 + 2 = 4
timeSeries=[1,2], duration=2:
第一次 [1,3)
第二次 [2,4)
gap=1
答案 = 1 + 2 = 3
第二个例子里 [2,3) 是重叠段,不能重复计算。
七、常见误区与追问
- 误区:每次攻击都直接加 duration。 重叠中毒时间会被重复计算。
- 误区:把端点当闭区间多算 1。 持续时间题更适合半开区间
[t,t+d)。 - 误区:忘记加最后一次 duration。 相邻差值只统计到每次攻击发生前的新增时间。
- 追问:为什么只看相邻攻击? 时间有序,重叠关系已经被相邻间隔完全决定。
- 追问:如果 timeSeries 不是有序怎么办? 需要先排序,否则相邻差值无意义。
- 追问:复杂度是多少? 单次扫描
O(n),额外空间O(1)。
八、加强记忆
提莫攻击就是最轻量的区间合并。每次攻击产生 [t,t+d),相邻攻击间隔小于 d 就重叠,新增只算间隔;间隔大于等于 d 就完整新增。循环里累加 min(duration,gap),最后补上最后一次完整持续时间。