最短长度子集

时间:2012-06-22 14:04:02

标签: arrays search

我有一个数组和一个数字N。

数组可以用数字0,1,2,3 .... N

填充

例如, arr = {1,0,2,3,1,0,2,4,3,1,0,2,4,3,0,0,0} //给定N = 4

我必须找到最小长度子阵列,其中包含所有数字1,2,... N。

例如,上面数组的答案应为{1,0,2,3,1,0, 2,4,3,1 ,0,2,4,3,0 ,0,0} // length = 4,索引为start = 6,end = 9,// 0 based

上述问题的一个可能答案是{1,0,2, 3,1,0,2,4 ,3,1,0,2,4,3,0,0, 0},但由于其长度为5,因此被拒绝.. 如果存在多于一个最小长度的子阵列,则应该是1次出现。 或者,如果数组在1,2..N之间不包含一个或多个数字,答案是“找不到子数组”。

这是我的python代码。它为某些情况(我不知道)产生了错误的答案...... 如果有人能说出我做错了什么。

shortlen=2000001 //initialise to INFINITY
shortstart=0 
matchln=len(match) //match is the array containing integers

while(i<matchln):
   if(match[i]>0):
    leng=0
    pos=[0]*n // array to keep status of found integers
    j=i
    start=i
    sums=0
    while(j<matchln and sums!=n):
        if(match[j]>0):
            if(pos[match[j]-1]==0): //only update status if the integer is not marked previously.
                pos.pop(match[j]-1)
                pos.insert(match[j]-1,1) //(match[j]-1) becuz array indexing is from 0.
                sums+=1


        j+=1

    leng=j-i

    if(j==matchln and sums!=n): // if the loop terminated,without marking all integers,that means we shouldn't proceed.
        break

    if(leng<shortlen): //if the length calculated is smaller then existing,then update it.
        shortlen=leng
        shortstart=start

i+=1

3 个答案:

答案 0 :(得分:1)

一种可能性是跟踪每个起始位置的最短长度。您可以通过对数组进行两次传递来完成此操作:

假设索引1..k你在位置后找到了一组数字(在1..N内)(每个位置设置不同),当你前进到位置k + 1时,你需要更新全部使用位置k + 1处的数字设置(*)(只要数字在1..N内)。一旦集合包含N个元素,您就会找到该起始位置的最短序列,记录该位置的长度。

(*)意识到对于具有完整集的位置,您不再需要迭代它们。此外,一旦一个位置的集合已满,之前的位置集也必须是满的,因此您可以保持一个滑动的“起始位置”以检查集合

您现在可以执行另一次传递来为每个位置选择最短的记录序列(您可以根据开始和序列长度计算结束位置)。

status = new array[arr.length] of Status // for score keeping
// initialize Status with: set <- empty, length <- n+1
startPos = 1 // sliding start position
// first pass
for i = 1..arr.length
  if arr[i] > 0 // within 1..N
    for j = startPos..i
      status[j].set.add(arr[i])
      if status[j].set.size == N // we have all numbers
        status[j].length = i-j;
         startPos = j+1

min = n+1 // for the shortest length
startPos = 1
// second pass
for i = 1..status.length
  if status[i].length < min
    min = status[i].length
    startPos = i

if min < n+1
  // found a winner
  print("start: " + startPos + ", end: " + startPos + min)

注意:上面代码中的索引从1开始(而不是从0开始)

答案 1 :(得分:0)

我想这可能会对你有所帮助。 您的问题的目标是将您的值转换为线性独立序列。我写了一个小代码来解决你的问题,它找到了你想要的序列的开头:

#include <stdio.h>
void main(){
/*By Volnei Klehm,
 Manaus-AM, Brazil
    2012
*/
long long arr[]={1,0,2,3,1,0,2,4,3,1,0,2,4,3,0,0,0};
long long power2arr[17]; /*same size or larger than arr*/
long long powerSum=0;
long long partialSum=0;
int count,count1,N;

int size_arr;
size_arr=sizeof(arr)/sizeof(long long);

/*the goal here is to find a way to represent your values as linear indepent ones,
      here a sequece of power of 2 is used, you can also use other ways to do it that not increases so dramatically in values.
      I use powers of 2 cause is more easy handled by computers, you can also use a sequence of sines, 
      cosines or any other math way that produces independets values. 
      For more informations I suggest you to take a look in linear algebra and/or digital signal processing books*/


/*Now it computes an independent basis*/

for(count=0 ; count<size_arr ; ++count){
    power2arr[count] = 1 << arr[count]; /*calculates 2^arr generating a set of independent numbers.*/

}

N=4; /*put here your N, for n=4 it will look for*/
/*Notice that deppending on your system, N cannot be too large,
      at certain point N values can make 2^N too large to be handled 
      by standard c/c++ types. Here is safe to use n up to 63.*/

/*now it gets the sum results of 2^0 + 2^1 + 2^2 ... + 2^N*/

++N; /* in C position starts at 0*/

for(count = 0 ; count < N ; count++)
    powerSum |= 1 << count;

for(count = 0 ; count<size_arr ; ++count){
    partialSum=0;
    for(count1 = count ; count1 < (count + N) ; count1++){
        if((count + N) > size_arr){
            printf("No occurrences found!\n");
            return;
        }
        partialSum |= power2arr[count1];    
    }
    if(partialSum==powerSum){
        printf("Ocurrence found at: %d\n", count);
        return;
    }
}

}

答案 2 :(得分:0)

如果允许额外的哈希表,可以一次完成。

基本上你在数组上保留两个指针: left right ,两者都指向数组中的第一个元素。

在每一轮中,我们首先向前移动右侧。第一次移动后,只要 right 指向与 left 相同的值,我们也会向前移动 left 。我们当然会跳过0。

在每一轮中,我们维护哈希表以查看从1到N的哪个值在区间[left,right]内,如果所有值都在区间内,我们得到区间长度。我们在整个过程中跟踪最小间隔长度。

时间复杂度为O(Nn)