给定一个整数数组和一个范围(低,高),找到所有 数组中连续的子序列,其范围内的和。
是否有比O(n ^ 2)更好的解决方案?
我尝试了很多,但找不到比O(n ^ 2)更好的解决方案。请帮我找到更好的解决方案或确认这是我们能做的最好的。
这就是我现在所拥有的,我假设范围被定义为[lo, hi]
。
public static int numOfCombinations(final int[] data, final int lo, final int hi, int beg, int end) {
int count = 0, sum = data[beg];
while (beg < data.length && end < data.length) {
if (sum > hi) {
break;
} else {
if (lo <= sum && sum <= hi) {
System.out.println("Range found: [" + beg + ", " + end + "]");
++count;
}
++end;
if (end < data.length) {
sum += data[end];
}
}
}
return count;
}
public static int numOfCombinations(final int[] data, final int lo, final int hi) {
int count = 0;
for (int i = 0; i < data.length; ++i) {
count += numOfCombinations(data, lo, hi, i, i);
}
return count;
}
答案 0 :(得分:16)
O(n)时间解决方案:
你可以扩展&#39;两个指针&#39;关于&#39;确切&#39;的想法版本的问题。我们将维护变量a
和b
,以便xs[i,a), xs[i,a+1), ..., xs[i,b-1)
表单上的所有区间在搜索范围[lo, hi]
中都有一个总和。
a, b = 0, 0
for i in range(n):
while a != (n+1) and sum(xs[i:a]) < lo:
a += 1
while b != (n+1) and sum(xs[i:b]) <= hi:
b += 1
for j in range(a, b):
print(xs[i:j])
由于O(n^2)
,这实际上是sum
,但我们可以通过首先计算前缀总和ps
来ps[i] = sum(xs[:i])
来轻松解决这个问题。然后sum(xs[i:j])
只是ps[j]-ps[i]
。
以下是使用[2, 5, 1, 1, 2, 2, 3, 4, 8, 2]
在[lo, hi] = [3, 6]
上运行上述代码的示例:
[5]
[5, 1]
[1, 1, 2]
[1, 1, 2, 2]
[1, 2]
[1, 2, 2]
[2, 2]
[2, 3]
[3]
[4]
这会及时运行O(n + t)
,其中t
是输出的大小。正如一些人已经注意到的那样,输出可以与t = n^2
一样大,即如果所有连续的子序列都匹配。
如果我们允许以压缩格式写出输出(输出对a,b
,其中所有子序列都是连续的),我们可以得到一个纯O(n)
时间算法。
答案 1 :(得分:8)
从这个problem开始:找到总和为x的所有连续子序列。我们需要的是类似的东西。
对于每个索引i,我们可以计算从0到i的段的总和,即x。所以,现在的问题是我们需要找到从0到i - 1,有多少段具有从(x - 低)到(x - 高)的和,并且它应该比O(n)快。因此,有几种数据结构可以帮助您在O(logn)中执行此操作,Fenwick tree和Interval tree。
所以我们需要做的是:
遍历从0到n的所有索引(n是数组的大小)。
在索引ith,计算从0到第i个索引的总和x,查询树以获得数字的总出现次数(x - 高,x - 低)。
将x添加到树中。
因此时间复杂度为O(n log n)
答案 2 :(得分:5)
您应该使用简单的动态编程和二进制搜索。要查找计数:
from bisect import bisect_left, bisect_right
def solve(A, start, end):
"""
O(n lg n) Binary Search
Bound:
f[i] - f[j] = start
f[i] - f[j'] = end
start < end
f[j] > f[j']
:param A: an integer array
:param start: lower bound
:param end: upper bound
:return:
"""
n = len(A)
cnt = 0
f = [0 for _ in xrange(n+1)]
for i in xrange(1, n+1):
f[i] = f[i-1]+A[i-1] # sum from left
f.sort()
for i in xrange(n+1):
lo = bisect_left(f, f[i]-end, 0, i)
hi = bisect_right(f, f[i]-start, 0, i)
cnt += hi-lo
return cnt
https://github.com/algorhythms/LintCode/blob/master/Subarray%20Sum%20II.py
要查找结果而不是计数,您只需要另一个哈希表来存储原始(未排序)f [i] - &gt;的映射。索引列表。
干杯。
答案 3 :(得分:0)
如果只有正数,您可以获得 O(nlogn): -
1. Evaluate cumulative sum of array
2. for i find total sum[j] in (sum[i]+low,sum[i]+high) using binary search
3. Total = Total + count
4. do 3 to 5 for all i
时间复杂度: -
Cumulative sum is O(N)
Finding sums in range is O(logN) using binary search
Total Time complexity is O(NlogN)
答案 4 :(得分:0)
如果所有整数都是非负数,则可以在O(max(size-of-input,size-of-output))
时间内完成。这是最佳的。
这是C中的算法。
void interview_question (int* a, int N, int lo, int hi)
{
int sum_bottom_low = 0, sum_bottom_high = 0,
bottom_low = 0, bottom_high = 0,
top = 0;
int i;
if (lo == 0) printf ("[0 0) ");
while (top < N)
{
sum_bottom_low += a[top];
sum_bottom_high += a[top];
top++;
while (sum_bottom_high >= lo && bottom_high <= top)
{
sum_bottom_high -= a[bottom_high++];
}
while (sum_bottom_low > hi && bottom_low <= bottom_high)
{
sum_bottom_low -= a[bottom_low++];
}
// print output
for (i = bottom_low; i < bottom_high; ++i)
printf ("[%d %d) ", i, top);
}
printf("\n");
}
除了标记为&#34;打印输出&#34;的最后一个循环外,每个操作执行O(N)次;对于打印的每个间隔,最后一个循环执行一次。如果我们只需要计算间隔而不打印它们,则整个算法变为O(N)
。
如果允许负数,那么O(N^2)
很难被击败(可能是不可能的)。
答案 5 :(得分:0)
yes in my opinion it can be in O(n)
struct subsequence
{
int first,last,sum;
}s;
function(array,low,high)
{
int till_max=0;
s.first=0;s.last=0;s.sum=0;
for(i=low;i<high;i++)
{
if(till_max+array[i]>array[i])
{
s.first=s.first;
s.last=i;
till_max+=array[i];
}
else
{
s.first=i;
s.last=i;
till_max=array[i];
}
if(till_max in range)
{
s.sum=till_max;
printf("print values between first=%d and last=%d and sum=%d",s.first,s.last,s.sum);
}
}
}
答案 6 :(得分:0)
O(NlogN)
就足够了。
对于连续的子序列,我认为这对子数组意味着。
我们维护一个前缀和列表prefix[i] = sum for the first i elements
。如何检查[low, high]
之间是否存在范围朗姆酒?我们可以使用二进制搜索。所以,
prefix[0] = array[0]
for i in range(1, N)
prefix[i] = array[i] + prefix[i-1];
idx1 = binarySearch(prefix, prefix[i] - low);
if (idx1 < 0) idx1 = -1 - idx1;
idx2 = binarySearch(prefix, prefix[i] - high);
if (idx2 < 0) idx2 = -1 - idx2;
// for any k between [idx1, idx2], range [k, i] is within range [low, high]
insert(prefix, prefix[i])
我们唯一需要关心的是,我们还需要插入新值,因此任何数组或链接列表都不可以。我们可以使用 TreeSet ,或实现自己的 AVL 树,二进制搜索和插入都将在O(logN)中进行。