解决方案
方法一:贪心 + 事件
思路
如果最左边的元素是 0,那么我们一定要翻转从位置 0 开始的子数组。类似的,如果最左边的元素是 1,我们不应该翻转从位置 0 开始的子数组。这证明了我们可以贪心地执行这一过程:在明确是否要反转第一个子数组之后(位置 0 至 K-1),我们可以考虑将数组中第一个元素(值为 1)移除,然后重复这个过程。
我们还可以做得更好。每次翻转一个子数组 A[i], A[i+1], ..., A[i+K-1]
,我们可以考虑这样的两种事件:第一种是 “开始事件”,标记位置 i
为我们翻转子数组的开始,另一种是 “结束事件” ,标记位置 i+K
是我们翻转子数组的结束。使用这些事件,我们就可以知道某一个位置被多少个重叠的翻转子数组覆盖了:它的数值等于 “开始事件” 的数量减去 “结束事件” 的数量。
算法
当我们翻转一个子数组的时候,让我们称翻转子数组的下标集合为一个区间。我们将维护一个变量 flip
,也就是覆盖当前位置的重叠区间数量。我们只关心 flip
对 2 取模之后的值。
当我们翻转从 i
开始的一个区间,我们在位置 i+K
创建一个 “结束事件” 的提示,表明在那里要把翻转状态置反。
更多细节请看行内注释。
复杂度分析
-
时间复杂度: ,其中 是数组
A
的长度。 -
空间复杂度: 。