我在面试问题主题中遇到了这个问题。这是一个问题:
给定两个整数数组A [1..n]和 B [1..m],找到最小窗口 在包含所有元素的A中 换句话说,找到一对< i,j> 这样A [i..j]包含B [1..m]。
如果A不包含所有元素 B,那么i,j可以返回-1。 A中的整数不必与B中的整数顺序相同。如果有多个最小窗口(不同,但具有相同的大小),那么它足以返回其中一个。
示例:A [1,2,5,11,2,6,8,24,101,17,8]和B [5,2,11,8,17]。该算法应返回i = 2(A中的索引为5)和j = 9(A中的索引为17)。
现在我可以想到两种变化。
我们假设B有重复。
此变体不考虑每个元素在B中出现的次数。它只检查B中出现的所有唯一元素,并找到满足上述问题的A中最小的对应窗口。例如,如果A [1,2,4,5,7]和B [2,2,5],这个变化并不打扰B中有两个2,只检查A中B的唯一整数即2和5因此返回i = 1,j = 3.
此变体考虑了B中的重复项。如果B中有两个2,那么它也希望在A中至少看到两个2。如果不是,则返回-1,-1。
当你回答时,请告诉我你要回答的变体。伪代码应该做。如果计算它很棘手,请提及空间和时间复杂度。提及您的解决方案是否假设数组索引也从1或0开始。
提前致谢。
答案 0 :(得分:6)
以下在对数因子方面可证明是最佳的。 (我相信对数因子是不能摆脱的,因此它是最优的。)
变量1只是变体2的一个特例,在从B中删除重复后,所有的多重性都是1。所以它足以处理后一种变体;如果你想要变体1,只需删除O(m log m)
时间内的重复项。在下文中,让m
表示B中不同元素的数量。我们假设m < n
,因为否则我们可以在固定时间内返回-1
。
对于A中的每个索引i
,我们会找到最小的索引s[i]
,使A[i..s[i]]
包含B[1..m]
,并具有正确的多重性。关键的观察结果是 s[i]
不减少,这就是我们可以在摊销的线性时间内完成的。
从i=j=1
开始。我们将在当前窗口(c[1], c[2], ... c[m])
中保留B的每个元素出现次数的元组A[i..j]
。我们还将保留一组S
个索引(1..m
的子集),其计数为“正确”(即k
,其中c[k]=1
位于变体1中,或者变体2中的c[k] = <the right number>
。
因此,对于i=1
,从j=1
开始,递增每个c[A[j]]
(如果A[j]
是B的元素),请检查c[A[j]]
是否现在“正确”,并相应地在j
中添加或删除S
。 S
尺寸为m
时停止。您现在最多s[1]
次发现O(n log m)
。 (有O(n)
j
个,每个设置操作都需要O(log m)
次。)
现在要计算连续的s[i]
,请执行以下操作。递增i
,递减c[A[i]]
,相应地更新S
,并在必要时增加j
,直到S
的尺寸再次m
为止。这为每个s[i]
提供了i
。最后,报告(i,s[i])
最小的s[i]-i
。
请注意,虽然您似乎可能会为每个O(n)
执行最多j
个步骤(递增i
),但第二个指针j
仅向右移动:因此,您可以增加j
的总次数最多为n
。 (这是amortised analysis。)每次增加j
时,您可能会执行一个花费O(log m)
时间的设置操作,因此总时间为O(n log m)
。所需空间用于保持计数元组,B元素集,集合S以及其他变量的常量数量,因此总共O(m)
。
有一个明显的O(m+n)
下界,因为您需要检查所有元素。所以唯一的问题是我们能否证明log
因素是必要的;我相信它是。
答案 1 :(得分:1)
这是我想到的解决方案(但它不是很整洁)。
我将使用问题中的示例来说明它。
设A [1,2,5,11,2,6,8,24,101,17,8]和B [5,2,11,8,17]
排序B.(所以B = [2,5,8,11,17])。此步骤需要O(log m)。
分配一个大小为A的数组C.迭代通过A的元素,在排序的B中对它进行二进制搜索,如果找到则在C中输入“在排序的B + 1中的索引”。如果找不到它,输入-1。完成此步骤后,
A = [1,2,5,11,2,6,8,24,101,17,8](无变化,易于引用)。
C = [-1,1,2,4,1,-1,3,-1,-1,5,3]
时间:(n log m),空间O(n)。
请留下有关一般方法以及3a和3b的评论。
答案 2 :(得分:1)
答案 3 :(得分:0)
struct Pair {
int i;
int j;
};
Pair
find_smallest_subarray_window(int *A, size_t n, int *B, size_t m)
{
Pair p;
p.i = -1;
p.j = -1;
// key is array value, value is array index
std::map<int, int> map;
size_t count = 0;
int i;
int j;
for(i = 0; i < n, ++i) {
for(j = 0; j < m; ++j) {
if(A[i] == B[j]) {
if(map.find(A[i]) == map.end()) {
map.insert(std::pair<int, int>(A[i], i));
} else {
int start = findSmallestVal(map);
int end = findLargestVal(map);
int oldLength = end-start;
int oldIndex = map[A[i]];
map[A[i]] = i;
int _start = findSmallestVal(map);
int _end = findLargestVal(map);
int newLength = _end - _start;
if(newLength > oldLength) {
// revert back
map[A[i]] = oldIndex;
}
}
}
}
if(count == m) {
break;
}
}
p.i = findSmallestVal(map);
p.j = findLargestVal(map);
return p;
}