查找具有相同数量的1和0的最大子序列二进制集

时间:2010-06-29 11:12:11

标签: c algorithm language-agnostic

我在互联网上发现了以下问题,并想知道我将如何解决它:

  

您将获得一个包含0和1的数组。找到O(n)时间和O(1)空间算法,找到具有相同数量的1和0的最大子序列。

     

示例:

     
      
  1. 10101010 -   满足问题的最长子序列是输入本身
  2.   
  3. 1101000 -   满足该问题的最长子序列是110100
  4.   

9 个答案:

答案 0 :(得分:13)

<强>更新

我必须完全改写我的回答。 (如果你赞成早期版本,那么,你被欺骗了!)

让我们再次总结一下这个简单的案例,以便将其排除在外:

  

找到包含的位字符串的最长前缀   相同数量的1和0   阵列。

这是微不足道的:需要一个简单的计数器,计算我们有多少1比0,并在保持这个时迭代bitstring。此计数器最后一次变为零的位置是最长搜索前缀的结束。 O(N)时间,O(1)空间。 (我现在完全相信这是原始问题所要求的。)

现在让我们切换到更难的问题版本:我们不再需要子序列作为前缀 - 它们可以从任何地方开始。

经过一番来回思考,我认为可能没有线性算法。例如,考虑前缀“111111111111111111 ...”。这些中的每一个可能是最长子序列的开始,没有候选子序列起始位置支配(即总是提供比其他任何位置更好的解决方案),因此我们不能丢弃它们中的任何一个(O(N)在任何一步,我们必须能够在O(1)时间内从线性多个候选者中选择最佳开始(其具有与当前位置相同数量的1和0) 。 事实证明这是可行的,也很容易实现,因为我们可以根据1s(+1)和0s(-1)的运行总和选择候选者,这最多只有N,我们可以存储我们在2N单元格中达到每个总和的第一个位置 - 请参阅下面的pmod的答案(yellowfog的评论和几何见解)。

没有发现这个技巧,我用一个缓慢但确定的算法替换了一个快速但错误的(因为正确的算法比错误的算法更好!):

  • 构建一个数组A,其中从开始到该位置的累计数为1,例如如果bitstring是“001001001”,那么数组将是[0,0,1,1,1,2,2,2,3]。使用这个,我们可以在O(1)中测试子序列(i,j)是否有效:isValid(i, j) = (j - i + 1 == 2 * (A[j] - A[i - 1]),即如果其长度是其中1s的两倍,则它是有效的。例如,子序列(3,6)是有效的,因为6 - 3 + 1 == 2 * A [6] - A [2] = 4.
  • 普通的旧双循环:

    maxSubsLength = 0 对于i = 1到N - 1   对于j = i + 1到N.     if isValid(i,j)... #maintain maxSubsLength   结束 端

通过跳过比当前maxSubsLength短的i / j序列,使用一些分支绑定可以加快一点,但渐近地这仍然是O(n ^ 2)。慢,但有一个很大的优点:正确!

答案 1 :(得分:7)

严格地说,答案是没有这样的算法,因为由相同数量的零和1组成的字符串的语言不规则。

当然,每个人都忽略了这样一个事实:在空间中存储一个幅度n的整数O(log n)并在空间中将其视为O(1)。 :-)几乎所有大的O,包括时间的,都充满(或者说是空的)缺失的log n因子,或等效地,他们认为n受机器字大小的限制,这意味着你真的在看一个有限的问题,一切都是O(1)

答案 2 :(得分:6)

新解决方案: 假设我们有n位输入位阵列2 * n大小的数组来保持位的位置。因此,数组元素的大小必须足够大,以保持最大位置数。对于256个输入位阵列,它需要256x2字节数组(字节足以保持255 - 最大位置)。

从位数组的第一个位置开始,我们使用规则将位置从数组的中间开始(索引为n):

<强> 1。如果我们传递“1”位则递增位置,当传递“0”位

时递减

<强> 2。遇到已初始化的数组元素 - 不要更改它并记住位置之间的差异(当前减去数组元素) - 这是局部最大序列的大小。

第3。每当我们达到局部最大值时,将其与全局最大值进行比较,如果后者较小则更新。

例如:位序列 0,0,0,1,0,1

   initial array index is n
   set arr[n] = 0 (position)
     bit 0 -> index--
   set arr[n-1] = 1 
     bit 0 -> index--
   set arr[n-2] = 2
     bit 0 -> index--
   set arr[n-3] = 3
     bit 1 -> index++
   arr[n-2] already contains 2 -> thus, local max seq is [3,2] becomes abs. maximum
      will not overwrite arr[n-2]
     bit 0 -> index--
   arr[n-3] already contains 3 -> thus, local max seq is [4,3] is not abs. maximum
     bit 1 -> index++
   arr[n-2] already contains 2 -> thus, local max seq is [5,2] is abs. max

因此,我们只通过整个位数组一次。 这解决了这个任务吗?

input:
    n - number of bits
    a[n] - input bit-array

track_pos[2*n] = {0,};
ind = n;
/* start from position 1 since zero has
  meaning track_pos[x] is not initialized */
for (i = 1; i < n+1; i++) {
    if (track_pos[ind]) {
        seq_size = i - track_pos[ind];
        if (glob_seq_size < seq_size) {
            /* store as interm. result */
            glob_seq_size = seq_size;
            glob_pos_from = track_pos[ind];
            glob_pos_to   = i;
        }
    } else {
        track_pos[ind] = i;
    }

    if (a[i-1])
        ind++;
    else
        ind--;
}

output:
    glob_seq_size - length of maximum sequence
    glob_pos_from - start position of max sequence
    glob_pos_to   - end position of max sequence

答案 3 :(得分:1)

在这个帖子(http://discuss.techinterview.org/default.asp?interview.11.792102.31)中,海报A.F.给出了一个在O(n)时间运行并使用O(sqrt(n log n))位的算法。

答案 4 :(得分:0)

暴力:以数组的最大长度开始计算o和l。如果等于l,你就完成了。否则将搜索长度减少1,并对缩短长度的所有子序列(即最大长度减去减少的长度)执行算法,依此类推。当减法为0时停止。

答案 5 :(得分:0)

正如用户“R ..”所指出的那样,严格来说,除非你忽略“log n”空间复杂性,否则没有解决方案。在下文中,我将考虑数组长度适合机器寄存器(例如64位字),并且机器寄存器的大小为O(1)。

需要注意的重要一点是,如果1比0更多,那么你要寻找的最大子序列必然包括所有的0,以及那么多的1。所以算法在这里:

符号:数组的长度为 n ,索引从0到 n-1 计算。

  1. 第一遍:计算1的数量( c1 )和0的数量( c0 )。如果 c1 = c0 ,则最大子序列是整个数组(算法结束)。否则,让 d 成为较少出现的数字( d = 0 ,如果 c0&lt; c1 ,否则 d = 1 )。
  2. 计算 m = min(c0,c1)* 2 。这是您要查找的子序列的大小。
  3. 第二遍:扫描数组以查找第一次出现 d 的索引 j
  4. 计算 k = max(j,n - m)。子序列从索引 k 开始,长度 m
  5. 请注意,可能有多种解决方案(最大长度的几个子序列符合标准)。

    简单来说:假设有1比0更多,那么我会考虑包含所有0的最小子序列。根据定义,该子序列被一束1包围。所以我从侧面抓住了足够的1个。

    编辑:正如所指出的,这不起作用......“重点”实际上是错误的。

答案 6 :(得分:-1)

尝试这样的事情:

/* bit(n) is a macro that returns the nth bit, 0 or 1. len is number of bits */
int c[2] = {0,0};
int d, i, a, b, p;
for(i=0; i<len; i++) c[bit(i)]++;
d = c[1] < c[0];
if (c[d] == 0) return; /* all bits identical; fail */
for(i=0; bit(i)!=d; i++);
a = b = i;
for(p=0; i<len; i++) {
  p += 2*bit(i)-1;
  if (!p) b = i;
}
if (a == b) { /* account for case where we need bits before the first d */
  b = len - 1;
  a -= abs(p);
}
printf("maximal subsequence consists of bits %d through %d\n", a, b);

完全未经测试但模数愚蠢的错误应该有效。基于我对托马斯答案的答复,在某些情况下答案失败了。

答案 7 :(得分:-1)

新解决方案: O(1)的空间复杂度和时间复杂度O(n ^ 2)

        int iStart = 0, iEnd = 0;
        int[] arrInput = { 1, 0, 1, 1, 1,0,0,1,0,1,0,0 };

        for (int i = 0; i < arrInput.Length; i++)
        {
            int iCurrEndIndex = i;
            int iSum = 0;
            for (int j = i; j < arrInput.Length; j++)
            {                    
                iSum = (arrInput[j] == 1) ? iSum+1 : iSum-1;
                if (iSum == 0)
                {
                    iCurrEndIndex = j;
                }

            }
            if ((iEnd - iStart) < (iCurrEndIndex - i))
            {
                iEnd = iCurrEndIndex;
                iStart = i;
            }
        }

答案 8 :(得分:-1)

我不确定你引用的数组是0和1的int数组还是bitarray?

如果是关于bitarray,这是我的方法:

int isEvenBitCount(int n)
{
    //n ... //Decimal equivalent of the input binary sequence
    int cnt1 = 0, cnt0 = 0;
    while(n){
        if(n&0x01) { printf("1 "); cnt1++;}
        else { printf("0 "); cnt0++; }
        n = n>>1;
    }
    printf("\n");
    return cnt0 == cnt1;
}

int main()
{
    int i = 40, j = 25, k = 35;

    isEvenBitCount(i)?printf("-->Yes\n"):printf("-->No\n");
    isEvenBitCount(j)?printf("-->Yes\n"):printf("-->No\n");
    isEvenBitCount(k)?printf("-->Yes\n"):printf("-->No\n");
}

使用按位运算,时间复杂度也几乎为O(1)。