在Ivan Smirnov的最佳答案基础上进行改进,这里是用Python编写的原型实现。
from bisect import bisect_left
import random
import time
def build_index(input):
n = len(input)
last = {}
next = []
for j in range(n):
i = n - j - 1
if input[i] in last:
next.append(last[input[i]])
else:
next.append(n)
last[input[i]] = i
next.reverse()
return next
def merge(left, right):
answer = []
p = 0
q = 0
while True:
if p == len(left):
if q == len(right):
return answer
else:
answer.append(right[q])
q += 1
elif q == len(right):
answer.append(left[p])
p += 1
elif left[p] < right[q]:
answer.append(left[p])
p += 1
else:
answer.append(right[q])
q += 1
def build_tree(input, l, r):
if r - l == 1:
return (input[l:r], None, None)
else:
mid = (l + r) // 2
left = build_tree(input, l, mid)
right = build_tree(input, mid, r)
merged = merge(left[0], right[0])
return (merged, left, right)
def query(index, tree_left, tree_right, query_l, query_r):
tree_width = tree_right - tree_left
if tree_width == 1:
if index[0][0] >= query_r:
return 1
else:
return 0
else:
mid = (tree_left + tree_right) // 2
if query_r <= mid:
return query(index[1], tree_left, mid, query_l, query_r)
elif query_l >= mid:
return query(index[2], mid, tree_right, query_l, query_r)
elif query_l <= tree_left and query_r >= tree_right:
arr = index[0]
return tree_width - bisect_left(arr, query_r)
else:
return query(index[1], tree_left, mid, query_l, query_r) + query(index[2], mid, tree_right, query_l, query_r)
def brute(input, i, j):
seen = {}
for k in range(i, j):
seen[input[k]] = True
return len(seen)
input = random.choices(list(range(8)), k=100000)
index = build_index(input)
tree = build_tree(index, 0, len(index))
n = len(input)
i = random.randint(0, n)
j = random.randint(i + 1, n)
t1 = time.time()
q = query(tree, 0, len(index), i, j)
t2 = time.time()
b = brute(input, i, j)
t3 = time.time()
print(q == b)
print(t2 - t1)
print(t3 - t2)
O(n)
是下限,因为您需要查看每个元素。 - saschaO(r-l)
。 - sascha