查找二进制字符串中包含相等数量的0和1的最大子字符串

时间:2019-02-04 11:53:09

标签: algorithm language-agnostic

最近一次采访中,我被要求编写一个程序来查找最大的子字符串,该子字符串在二进制字符串中包含相等数量的01

例如:

如果给定的字符串为1010111,则输出将为1010,因为它包含2个0和2个1 s。

我做了很多尝试,但无论如何都无法为该问题构建算法。

有人可以给我一个开始的方法,以解决这个问题或使用什么数据结构吗?

4 个答案:

答案 0 :(得分:8)

以下内容将在 O(n)时空中工作, n 是字符串中的字符数。

  • 跟踪您在balance1时间中看到的0string当前first(或失衡)的情况字符串具有相同的余额(一个数组或映射最多包含n个条目)
  • 重复string,然后为每个字符...
    • 更新balance,将"1"计为1,将"0"计为-1,反之亦然
    • 检查是否曾经遇到过相同的balance
    • 如果差异大于当前的best,请记住新的最长子字符串
    • 如果您尚未达到平衡,请记住它是当前的first职位

Python中的示例代码:

string = "1010111000"
first = {0: 0}  # map or array; 0th element is 0
balance = 0     # initially, 1 and 0 are balanced
best = ""       # best substring found
for i, c in enumerate(string):             # (i, c) = (index, character)
    balance += 1 if c == "1" else -1       # update balance
    if balance not in first:               # first time we see this balance?
        first[balance] = i+1               # add next(!) position to map/array
    elif i - first[balance] > len(best):   # otherwise, if new longest substring
        best = string[first[balance]:i+1]  # update best with slice of string
    print(i, c, balance, best, first)      # debugging/demo output

输出:

0 1 1  {0: 0, 1: 1}
1 0 0 10 {0: 0, 1: 1}
2 1 1 10 {0: 0, 1: 1}
3 0 0 1010 {0: 0, 1: 1}
4 1 1 1010 {0: 0, 1: 1}
5 1 2 1010 {0: 0, 1: 1, 2: 6}
6 1 3 1010 {0: 0, 1: 1, 2: 6, 3: 7}
7 0 2 1010 {0: 0, 1: 1, 2: 6, 3: 7}
8 0 1 01011100 {0: 0, 1: 1, 2: 6, 3: 7}
9 0 0 1010111000 {0: 0, 1: 1, 2: 6, 3: 7}

答案 1 :(得分:4)

我会这样处理

  • 初始化可变整数和且maxlength = minimum

  • 定义一个以sum为键且索引为值的hashmap

  • 对于给定字符串中的每个值

    • sum + = arr [i] == 0?然后加-1,否则加1

    • 如果总和为0,则maxlength = maxlength或index + 1,因为这是一个可能的答案

    • 否则,如果字典包含该和值,maxlength = maxlength或 (i -index hash [sum])较早发现的和值有助于结果。

    • 如果sum的值不在哈希图中,则更新哈希图 索引

    • 返回最大长度。

这是我上面提到的示例,在代码working example中,您可以尝试更改测试用例,以了解其对各种测试用例的工作方式,还尝试打印哈希图并通过以下方式进行跟踪来加深了解。

答案 2 :(得分:2)

就最佳化而言,这种解决方案不是最佳解决方案,但是在匆忙和面试的压力下,可以快速地思考,绘制和解释这种解决方案。

我要嵌套2个循环。

1从0到len-2(递增)(最小长度应为2)

1从len到上一个循环值+ 2(递减)(最小长度应为2)

获取循环的相应迭代器的子字符串

计算字符是否相等。

然后,如果为true,则与存储的结果长度进行比较,如果长度较大,则覆盖结果。

0100为例,将对这些值进行测试:

// result = ''
0100 //not balanced
010  //not balanced
01   //balanced AND length is greated than result's one. result = '01'
 100 //not balanced
 10  //balanced BUT length is not greated than result's one
  00 //not balanced

JavaScript示例(我对其进行了一些调整以优化迭代次数,但方法相同):

var iterations = 0;

function IsBalanced(input, char1, char2)
{
    if (input.length % 2 != 0) // odd length can't be balanced
    {
      ++iterations;
      return (false);
    }
    let char1count = 0;
    let char2count = 0;
    let len = input.length;
    for (let i = 0; i < len; ++i)
    {
        ++iterations;
        if (input[i] == char1)
            ++char1count;
        else
            ++char2count;
    }
    return (char1count == char2count);
}

function findLargest(input, char1, char2)
{
    let len = input.length;
    let result = '';
    for (let k = 0; k < len - 1; ++k)
    {
        //        This is a tweak to reduce the number of iterations
        //  To avoid testing a substring smaller than the current result
        //                           |
        //                           |
        //                v----------------------v
        for (let l = len; l - k > result.length && l > k + 1; --l)
        {
            tempResult = input.substring(k, l);
            if (IsBalanced(tempResult, char1, char2) && tempResult.length > result.length)
                result = tempResult;
        }
    }
    return (result);
}

console.log("Input : 1010111 - result : " + findLargest("1010111", "1", "0") + " original size : " + "1010111".length + " - iterations : " + iterations);
iterations = 0;
console.log("Input : ababaaa - result : " + findLargest("ababaaa", "a", "b") + " original size : " + "ababaaa".length + " - iterations : " + iterations);
iterations = 0;
console.log("Input : 00100100 - result : " + findLargest("00100100", "1", "0") + " original size : " + "00100100".length + " - iterations : " + iterations);
iterations = 0;
console.log("Input : 1111100000 - result : " + findLargest("1111100000", "1", "0") + " original size : " + "1111100000".length + " - iterations : " + iterations);
iterations = 0;
console.log("Input : 0001111111111010001111100000000001111111111 - result : " + findLargest("0001111111111010001111100000000001111111111", "1", "0") + " original size : " + "0001111111111010001111100000000001111111111".length + " - iterations : " + iterations);
iterations = 0;
console.log("Input : 0000000000000000000000000000000000000000000000000001 - result : " + findLargest("0000000000000000000000000000000000000000000000000001", "1", "0") + " original size : " + "0000000000000000000000000000000000000000000000000001".length + " - iterations : " + iterations);

答案 3 :(得分:0)

一种非常简单的方法:

  • 将零处理为“ -1”
  • 使用“开始”和“结束”作为矩阵的行和列变量
  • 找到每个可能的起点和终点之间的数字总和(重要的是首先以最小的数字进行此操作,然后使用这些结果找到后面的总和,以确保这保持“恒定”速度[O( 1)]过程。
  • 通过首先考虑具有最大数字缺口(对应于最大子字符串)的行/列来遍历表格,直到得出答案为0
  • 第一个结果为0对应于最大长度等于一和零

简单和O(n ^ 2)的时间和空间复杂度。

编辑:迭代过程[项目符号4]不会改变最坏情况下的复杂度,但是我上面描述的方式可以创建更好的平均情况下的速度。数组可以可视化为三角矩阵,并且可以像这样(C ++样式)进行迭代:

// comment: n = number of 1's and 0's in the string;

int start = 0, end = 0; 

for ( int distance = n - 1; distance > 0; distance = distance - 1 )
{
    int row = 0, column = distance;

    while ( column < n )
    {
        if ( array [ row ] [ column ] == 0 )
        {
            start = row;
            end = column;

            // comment: Largest substring of equal-ones-and-zeros found

            return std :: make_pair ( start, end );
        }

        row = row + 1;
        column = column + 1;
    }
}