这是Given a sequence of N numbers ,extract number of sequences of length K having range less than R?
的后续问题我基本上需要向量v作为大小为N的答案,使得V [i]表示长度为i的序列的数量,其具有范围<= R。
答案 0 :(得分:1)
传统上,在递归解决方案中,您将计算K = 0,K = 1的解,然后在后续元素之间找到某种递归关系,以避免每次从头开始重新计算解决方案。
但是在这里我认为,从另一方面攻击问题会很有趣,因为传播的属性:
给定一个R(或更少)的扩散序列,任何子序列的扩散都不如R
因此,我首先建立一个从每个索引开始的扩展R的最长子序列的列表。我们将此列表M
称为M[i] = j
,其中j
是S
(原始序列)S[j] - S[i] <= R
中较高的索引。这将是O(N)。
现在,对于任何i
,从K
开始的长度为i
的序列数量为0
或1
,这取决于{是否{ {1}}大于K
或不大于M[i] - i
。对M
(从0
到N-K
)的简单线性传递给出了答案。这又是O(N)。
因此,如果我们调用V
生成的向量,V[k]
表示K
中S
的长度R
的子序列数,其差异小于for i in [0, len(M)]:
for k in [0, M[i] - i]:
++V[k]
,然后我们可以通过M:
M[i] - i
算法很简单,但更新次数可能相当艰巨。在最坏的情况下,假设N - i
等于{{1}},则为O(N * N)复杂度。您需要一个更好的数据结构(可能是Fenwick树的改编版)来使用这种算法,从而降低计算这些数字的成本。
答案 1 :(得分:0)
如果您正在寻找连续序列,请尝试递归执行:设置范围低于R的K长度子序列包含在(K-1) - 长度子序列集中。
在K = 0时,您有N个解决方案。 每次增加K,你追加(resp.prepend)下一个(resp.previous)元素,检查它是否低于R,并将其存储在一个集合中(查找重复项!)或丢弃它取决于结果。
如果认为在最坏的情况下该算法的复杂度为O(n * n),尽管平均可能更好。
答案 2 :(得分:0)
从一个更简单的问题开始:计算序列的最大长度,从每个索引开始并具有等于R的范围。
为此,让第一个指针指向数组的第一个元素。增加第二个指针(也从数组的第一个元素开始),而指针之间的序列具有范围,小于或等于R.将每个数组元素(由第二个指针传递)推送到min-max-queue,由一对mix-max-stacks,在this answer中描述。当min-max-queue报告的max和min值之间的差异超过R时,停止增加第二个指针,增加V[ptr2-ptr1]
,增加第一个指针(删除元素,由它指向,从min-max-queue),以及继续增加第二个指针(保持范围受控制)。
当第二个指针离开数组的边界时,对所有剩余的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;
}
答案 3 :(得分:0)
我认为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值,你会得到更好的效果。