我认为不可能将所有这些值存储在 O(n) 中。但是很容易在 O(n) 中创建一个可以回答的结构,在 O(1) 中查询“有多少子集,其中 A[i] 是最大元素”。
天真的版本:
想想简单的策略:要知道某个 A[i] 有多少这样的子集,您可以使用一个简单的 O(n) 算法来计算数组左侧和右侧有多少元素小于一个[我]。比方说:
A = [... 10 1 1 1 5 1 1 10 ...]
这个5
向上的左边有 3 个元素,右边有 2 个元素。由此我们知道有一些4*3=12
子数组5
的最大值是最大值。因为左边和右边4*3
都有子数组。0..3
0..2
优化版:
这种简单的检查版本将对每个元素进行 O(n) 次操作,所以毕竟 O(n^2)。如果我们可以一次通过 O(n) 计算所有这些长度不是很好吗?
幸运的是,有一个简单的算法。只需使用堆栈。正常遍历数组(从左到右)。将每个元素索引放入堆栈。但是在放之前,把所有值小于当前值的索引都去掉。当前索引之前的剩余索引是最近的较大元素。
要在右侧找到相同的值,只需向后遍历数组。
这是一个示例 Python 概念验证,它显示了该算法的实际应用。我还实现了 naïve 版本,因此我们可以交叉检查优化版本的结果:
from random import choice
from collections import defaultdict, deque
def make_bounds(A, fallback, arange, op):
stack = deque()
bound = [fallback] * len(A)
for i in arange:
while stack and op(A[stack[-1]], A[i]):
stack.pop()
if stack:
bound[i] = stack[-1]
stack.append(i)
return bound
def optimized_version(A):
T = zip(make_bounds(A, -1, xrange(len(A)), lambda x, y: x<=y),
make_bounds(A, len(A), reversed(xrange(len(A))), lambda x, y: x<y))
answer = defaultdict(lambda: 0)
for i, x in enumerate(A):
left, right = T[i]
answer[x] += (i-left) * (right-i)
return dict(answer)
def naive_version(A):
answer = defaultdict(lambda: 0)
for i, x in enumerate(A):
left = next((j for j in range(i-1, -1, -1) if A[j]>A[i]), -1)
right = next((j for j in range(i+1, len(A)) if A[j]>=A[i]), len(A))
answer[x] += (i-left) * (right-i)
return dict(answer)
A = [choice(xrange(32)) for i in xrange(8)]
MA1 = naive_version(A)
MA2 = optimized_version(A)
print 'Array: ', A
print 'Naive: ', MA1
print 'Optimized:', MA2
print 'OK: ', MA1 == MA2