给定一个N个数的数组,求出所有长度的序列在R范围内的个数

Given an array of N numbers,find the number of sequences of all lengths having the range of R?

本文关键字:范围内 一个 数组      更新时间:2023-10-16

这是给定一个N个数字的序列,提取长度为K且范围小于R的序列的数量的后续问题?

我基本上需要向量v作为大小为N的答案,使得v[I]表示长度为I的序列的数量=R.

传统上,在递归解中,您会计算K=0,K=1的解,然后在后续元素之间找到某种递归关系,以避免每次从头开始重新计算解。

然而,在这里,我相信也许从另一边攻击这个问题会很有趣,因为传播的性质:

给定一个扩频R(或更小)的序列,任何子序列的扩频也低于R

因此,我将首先建立一个从每个索引开始的排列R的最长子序列的列表。让我们将这个列表称为M,并具有M[i] = j,其中jS(原始序列)中S[j] - S[i] <= R的较高索引。这将是O(N)。

现在,对于任何i,从i开始的长度为K的序列的数量是01,并且这取决于K是否大于M[i] - i。通过对M(从0N-K)的简单线性传递给出了答案。这又是O(N)。

因此,如果我们将V称为结果向量,其中V[k]表示S中长度为K的子序列的数量,其扩展小于R,那么我们可以在M:上进行单次迭代

for i in [0, len(M)]:
for k in [0, M[i] - i]:
++V[k]

该算法很简单,但更新的数量可能相当惊人。在最坏的情况下,假设M[i] - i等于N - i,则为O(N*N)复杂度。你需要一个更好的数据结构(可能是对芬威克树的改编)来使用这种算法,从而降低计算这些数字的成本。

如果您正在寻找连续序列,请尝试递归执行:范围小于R的K-长度子序列集包含在(K-1)-长度子序列集中。

在K=0时,有N个解。每次增加K时,都会附加(resp.prepend)下一个(resp.previous)元素,检查其范围是否低于R,然后将其存储在一个集合中(寻找重复项!)或根据结果丢弃它。

如果认为在最坏的情况下,该算法的复杂度为O(n*n),尽管平均而言可能更好。

我认为Matthieu在寻找所有具有排列R的序列时有正确的答案。

由于你只寻找长度为K的序列,你可以做得更好一点。与其看从i开始的最大序列,不如看看从i开始长度为K的序列,看看它是否有范围R。对每个i都这样做,你就得到了所有长度为K、排列为R的序列。

您不需要浏览整个列表,因为长度为K的序列的最新起点是n-K+1。所以复杂性类似于(n-K+1)*K=n*K-K*K+K。对于K=1,这是n,对于K=n,它是n。对于K=n/2,它是n*n/2-n*n/4+n/2=n*n/2+n/2,我认为这是最大值。所以,当这仍然是O(n*n)时,对于K的大多数值,你会得到更好的结果。

从一个更简单的问题开始:计算序列的最大长度,从每个索引开始,其范围等于R。

为此,让第一个指针指向数组的第一个元素。增加第二个指针(也从数组的第一个元素开始),同时指针之间的序列的范围小于或等于R。将第二个指示器传递的每个数组元素推到由一对混合-最大堆栈组成的最小-最大队列,如本答案所述。当最小-最大队列报告的最大值和最小值之差超过R时,停止增加第二个指针,增加V[ptr2-ptr1],增加第一个指针(从最小-最大排队中删除它所指向的元素),并继续增加第二指针(控制范围)。

当第二个指针离开数组的边界时,为所有剩余的ptr1递增V[N-ptr1](相应的范围可以小于或等于R)。要将小于R的所有其他范围相加,请从数组的末尾开始计算数组V[]的累积和。

时间复杂性和空间复杂性都是O(N)。

伪代码:

p1 = p2 = 0;
do {
do {
min_max_queue.push(a[p2]);
++p2;
} while (p2 < N && min_max_queue.range() <= R);
if (p2 < N) {
++v[p2 - p1 - 1];
min_max_queue.pop();
++p1;
}
} while (p2 < N);
for (i = 1; i <= N-p1; ++i) {
++v[i];
}
sum = 0;
for (j = N; j > 0; --j) {
value = v[j];
v[j] += sum;
sum += value;
}