问题在于,给定长度为N
的数组,我们必须找到长度为W
的所有子序列,以便那些W
元素在排序时形成算术级数因此对于像[1,4,6,3,5,2,7,9]
和W
这样的数组为5,切片[4,6,3,5,2]
可以被视为一个这样的子序列,因为在排序时,它会产生{{1} ,具有共同差异的AP 1.
想到的最直接的解决方案是为每个新元素设置一个滑动窗口,弹出旧元素,推送新元素,对窗口进行排序,如果对于该窗口,[2,3,4,5,6]
,则它就是这样一个子序列。但是,它需要window[w-1] - window[0] + 1 = w
时间,而Codechef处的解决方案提出了使用双端队列的O(NlogN)
时间算法。我很难理解算法,正在推送和弹出的内容,以及为什么如此,以及它如何按排序顺序维护窗口而无需借助每个新元素。任何人都可以解释一下吗?
答案 0 :(得分:1)
如果max(segment) - min(segment) + 1 = W
表示某段有效,则表示您是正确的。因此,问题减少为查找W
中所有长度O(N)
段的最小值和最大值。
为此,我们可以使用deque D
。假设我们想找到分钟。我们将元素的索引存储在D
中,假设基于0的索引。让A
成为原始数组。
for i = 0 to N - 1:
if D.first() == i - W:
D.popFirst() <- this means that the element is too old,
so we no longer care about it
while not D.empty() and A[ D.last() ] >= A[i]:
D.popLast()
D.pushBack(i)
对于每个i
,这将为您提供[i - W + 1, i]
中作为索引D.first()
元素的最小值。
popFirst()
从D
中删除第一个元素。当D
中的第一个元素与W
的距离超过i
时,我们必须执行此操作,因为它不会影响上述区间中的最小值。
popLast()
从D
中删除最后一个元素。我们这样做是为了维护排序顺序:如果D
中的最后一个元素是大于A[i]
的元素的索引,那么在i
末尾添加D
会打破秩序。因此,我们必须继续删除最后一个元素,以确保D
保持排序。
pushBack()
在D
末尾添加了一个元素。添加后,D
肯定会保持排序。
这是O(1)
(要查找分钟,上面的伪代码为O(n)
),因为每个元素最多只会被推送到D
{/ 1}}一次。
这是有效的,因为D
将始终是索引的滑动窗口,并按A
中的关联值排序。当我们处于一个会破坏这个顺序的元素时,我们可以从D
(滑动窗口)中弹出元素,直到恢复顺序。由于新元素小于我们弹出的元素,因此无法为解决方案做出贡献。
请注意,即使没有我使用的方法,您也可以通过保留与D
:start
和end
相关联的两个指针来实现此功能。然后将D
设为长度为N
的数组,您就完成了。