确定包含另一个数组B的所有元素的数组A的索引i..j的算法

时间:2009-05-29 13:21:16

标签: algorithm

我在面试问题主题中遇到了这个问题。这是一个问题:

  

给定两个整数数组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有重复。

  1. 此变体不考虑每个元素在B中出现的次数。它只检查B中出现的所有唯一元素,并找到满足上述问题的A中最小的对应窗口。例如,如果A [1,2,4,5,7]和B [2,2,5],这个变化并不打扰B中有两个2,只检查A中B的唯一整数即2和5因此返回i = 1,j = 3.

  2. 此变体考虑了B中的重复项。如果B中有两个2,那么它也希望在A中至少看到两个2。如果不是,则返回-1,-1。

  3. 当你回答时,请告诉我你要回答的变体。伪代码应该做。如果计算它很棘手,请提及空间和时间复杂度。提及您的解决方案是否假设数组索引也从1或0开始。

    提前致谢。

4 个答案:

答案 0 :(得分:6)

复杂性

时间:O((m + n)log m)

空间:O(m)

以下在对数因子方面可证明是最佳的。 (我相信对数因子是不能摆脱的,因此它是最优的。)

变量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中添加或删除SS尺寸为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]

  1. 排序B.(所以B = [2,5,8,11,17])。此步骤需要O(log m)。

  2. 分配一个大小为A的数组C.迭代通过A的元素,在排序的B中对它进行二进制搜索,如果找到则在C中输入“在排序的B + 1中的索引”。如果找不到它,输入-1。完成此步骤后,

  3. 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)。

    1. 找到C中包含从1到m的所有数字的最小窗口。为了找到窗口,我可以想到两个方向: 一个。一种面向位的方法,其中我设置对应于每个位置的位,最后通过某种ANDing检查。 湾创建另一个大小为m的数组D,通过C,当我在C中遇到p时,递增D [p]。用它来查找窗口。
    2. 请留下有关一般方法以及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;
}