找到以随意方式存储的连续增加的子序列

时间:2012-09-26 09:10:20

标签: algorithm sorting sliding-window

问题在于,给定长度为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)时间算法。我很难理解算法,正在推送和弹出的内容,以及为什么如此,以及它如何按排序顺序维护窗口而无需借助每个新元素。任何人都可以解释一下吗?

1 个答案:

答案 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(滑动窗口)中弹出元素,直到恢复顺序。由于新元素小于我们弹出的元素,因此无法为解决方案做出贡献。

请注意,即使没有我使用的方法,您也可以通过保留与Dstartend相关联的两个指针来实现此功能。然后将D设为长度为N的数组,您就完成了。