不止是算法题:从‘小美的区间删除’聊聊如何用前缀和+二分搞定约束计数问题
从“小美的区间删除”到双约束计数前缀和与二分的艺术当美团春招笔试中出现“小美的区间删除”这道题时不少算法爱好者眼前一亮——它看似是一道普通的数组操作题实则暗藏玄机。题目要求删除一个区间后剩余元素乘积末尾至少有k个0。这背后涉及到的不仅是简单的区间处理更是一场关于前缀和优化与二分查找的思维盛宴。1. 问题本质与数学转化乘积末尾0的个数由乘数中2和5因子的最小值决定。设原数组总2因子数为total2总5因子数为total5删除区间中的2和5因子数分别为x2和x5。根据题意min(total2 - x2, total5 - x5) ≥ k这等价于两个独立不等式x2 ≤ total2 - k x5 ≤ total5 - k关键突破点在于将原问题转化为寻找所有满足上述两个条件的子区间。这种双约束条件的处理正是本问题的核心难点。提示在算法题中将复杂条件分解为多个简单条件的交集是常见的解题思路2. 暴力解法与滑动窗口的局限性最直观的解法是枚举所有可能的子区间ans 0 for i in range(n): x2 x5 0 for j in range(i, n): x2 factor_2_nums[j] x5 factor_5_nums[j] if x2 total2 - k and x5 total5 - k: ans 1 else: break这种方法时间复杂度为O(n²)对于n1e5的数据量显然不可行。滑动窗口似乎是个优化方向但实际尝试会发现i j -1 x2 x5 0 while True: if x2 total2 - k and x5 total5 - k: ans 1 j 1 if j n: break x2 factor_2_nums[j] x5 factor_5_nums[j] else: i 1 x2 - factor_2_nums[i] x5 - factor_5_nums[i]这种方法会遗漏某些有效区间因为双约束条件导致窗口移动策略复杂化。3. 前缀和二分的精妙解法3.1 前缀和预处理首先计算2和5因子的前缀和数组from itertools import accumulate prefix2 [0] list(accumulate(factor_2_nums)) prefix5 [0] list(accumulate(factor_5_nums))这样任意区间[i,j]的因子和可以O(1)计算x2 prefix2[j1] - prefix2[i] x5 prefix5[j1] - prefix5[i]3.2 二分查找应用对于每个左端点i我们需要找到最大的j满足prefix2[j] ≤ prefix2[i] (total2 - k) prefix5[j] ≤ prefix5[i] (total5 - k)使用bisect_right实现from bisect import bisect_right ans 0 for i in range(n1): j2 bisect_right(prefix2, prefix2[i] (total2 - k)) - 1 j5 bisect_right(prefix5, prefix5[i] (total5 - k)) - 1 ans min(j2, j5) - i这种方法将时间复杂度优化到O(n log n)完美解决大规模数据问题。4. 模式扩展与实际应用这种前缀和二分的组合拳在解决双约束问题时展现出强大威力。类似的问题变体包括寻找和不超过K且长度最大的子数组统计满足多个条件的子区间数量带权重的区间统计问题实战技巧当遇到区间统计问题时首先考虑前缀和预处理对于双约束条件尝试将其分解为独立条件处理二分查找适用于单调性问题能有效降低时间复杂度表格对比不同解法性能方法时间复杂度适用场景暴力枚举O(n²)小规模数据滑动窗口O(n)单约束条件前缀和二分O(n log n)多约束条件在最近的科技公司面试中这类问题频繁出现。例如某大厂的题目给定数组和k求有多少子数组满足最大值与最小值的差不超过k同样可以运用类似的思维模式结合单调栈和二分查找来优化。