我发现了这个面试问题,我无法想出比O(N ^ 2 * P)好的算法:
给定P自然数(1,2,3,...,P)的向量和另一个长度为N的向量,其元素来自第一个向量,找到第二个向量中最长的子序列,这样所有元素均匀分布(频率相同)。
示例:(1,2,3)和(1, 2,1,3,2,1,3,1,2,3 ,1)。最长的子序列在区间[2,10]中,因为它包含来自第一个序列的所有元素具有相同的频率(1个出现3次,2次3次,3次3次)。
时间复杂度应为O(N * P)。
答案 0 :(得分:49)
“子序列”通常表示不连续。我假设你的意思是“子列表”。
这是一个O(N P)算法,假设我们可以散列(假设不需要;我们可以改为基数排序)。扫描阵列,保持每个号码的运行总计。对于你的例子,
1 2 3
--------
0 0 0
1
1 0 0
2
1 1 0
1
2 1 0
3
2 1 1
2
2 2 1
1
3 2 1
3
3 2 2
1
4 2 2
2
4 3 2
3
4 3 3
1
5 3 3
现在,通过减去最小元素来标准化每一行。结果是
0: 000
1: 100
2: 110
3: 210
4: 100
5: 110
6: 210
7: 100
8: 200
9: 210
10: 100
11: 200.
准备两个哈希值,将每一行映射到它出现的第一个索引以及它出现的最后一个索引。通过键迭代并使用最后一个键 - 最后一个键。
000: first is at 0, last is at 0
100: first is at 1, last is at 10
110: first is at 2, last is at 5
210: first is at 3, last is at 9
200: first is at 8, last is at 11
最佳密钥为100,因为其子列表的长度为9.子列表是第10个元素的第(1 + 1)个元素。
这是因为子列表是平衡的,当且仅当它的第一个和最后一个非标准化的直方图是相同的,直到添加一个常量,当且仅当第一个和最后一个标准化直方图相同时才会出现。
答案 1 :(得分:6)
如果内存使用率不重要,那很容易......
您可以指定矩阵维N*p
并在列(i)中保存与(i)<之间的p
元素相对应的值对应的值/ strong>第二个向量中的第一个元素......
完成矩阵后,您可以搜索列i
中列i
中的所有元素都没有不同的列i
。最大{{1}}就是答案。
答案 2 :(得分:3)
随机化,您可以将其降低到线性时间。想法是用随机整数替换每个P值,使得这些整数总和为零。现在查找两个相等的前缀和。这允许一些假阳性的可能性很小,我们可以通过检查输出来补救。
在Python 2.7中:
# input:
vec1 = [1, 2, 3]
P = len(vec1)
vec2 = [1, 2, 1, 3, 2, 1, 3, 1, 2, 3, 1]
N = len(vec2)
# Choose big enough integer B. For each k in vec1, choose
# a random mod-B remainder r[k], so their mod-B sum is 0.
# Any P-1 of these remainders are independent.
import random
B = N*N*N
r = dict((k, random.randint(0,B-1)) for k in vec1)
s = sum(r.values())%B
r[vec1[0]] = (r[vec1[0]]+B-s)%B
assert sum(r.values())%B == 0
# For 0<=i<=N, let vec3[i] be mod-B sum of r[vec2[j]], for j<i.
vec3 = [0] * (N+1)
for i in range(1,N+1):
vec3[i] = (vec3[i-1] + r[vec2[i-1]]) % B
# Find pair (i,j) so vec3[i]==vec3[j], and j-i is as large as possible.
# This is either a solution (subsequence vec2[i:j] is uniform) or a false
# positive. The expected number of false positives is < N*N/(2*B) < 1/N.
(i, j)=(0, 0)
first = {}
for k in range(N+1):
v = vec3[k]
if v in first:
if k-first[v] > j-i:
(i, j) = (first[v], k)
else:
first[v] = k
# output:
print "Found subsequence from", i, "(inclusive) to", j, "(exclusive):"
print vec2[i:j]
print "This is either uniform, or rarely, it is a false positive."
答案 3 :(得分:2)
这是一个观察:你不能得到一个均匀分布的序列,它不是P
的倍数。这意味着您只需要检查N
,P
,2P
...长 - 3P
这样的序列(N/P)^2
的子序列。
答案 4 :(得分:0)
你可以通过增强uty的解决方案将其降低到O(N)时间,而不依赖于P.
对于每一行,不是存储每个元素的规范化计数,而是存储规范化计数的散列,同时仅保留当前索引的规范化计数。在每次迭代期间,您需要首先更新标准化计数,如果计数的每次递减都是在递增时支付,则该计数的摊销成本为O(1)。接下来,您重新计算哈希值。这里的关键是散列需要在被散列的元组的一个元素的递增或递减之后容易地更新。
this question的答案中显示了至少一种有效地进行散列的方法,具有良好的理论独立性保证。注意,计算指数以确定要添加到散列的量的O(lg P)成本可以通过预先计算以初始运行时间为O(P)的预计算的指数来计算,从而得到总计运行时间为O(N + P)= O(N)。