【问题标题】:max consecutive 1 or 0 that can be obtained by flipping one bit of binary nubmer通过翻转二进制数字的一位可以获得最大连续1或0
【发布时间】:2016-11-02 09:04:19
【问题描述】:

给定一个二进制数,找出通过翻转一位(1 或 0)可以获得的最大连续 1 或 0

给出的代码是

    public int getMacCon(string[] A)
    {
        int n = A.Length;
        int result = 0;
        for (int i = 0; i < n - 1; i++)
        {
            if (A[i] == A[i + 1])
                result = result + 1;
        }
        int r = -2;

        for (int i = 0; i < n; i++)
        {
            int count = 0;
            if (i > 0)
            {
                if (A[i - 1] != A[i])
                    count = count + 1;
                else
                    count = count - 1;
            }
            if (i < n - 1)
            {
                if (A[i + 1] != A[i])
                    count = count + 1;
                else
                    count = count - 1;
            }
            r = Math.Max(r, count);
        }
        return result + r;
    }

我很难弄清楚这里的逻辑。特别是下面给出的部分

    for (int i = 0; i < n - 1; i++)
    {
        if (A[i] == A[i + 1])
            result = result + 1;
    }

如果有人可以向我解释此解决方案中的逻辑,我将不胜感激。 谢谢

【问题讨论】:

  • 顺便说一句,显示的代码对长度为 0、1 或 2 的字符串给出了错误的答案;应该有switch(n) { case 0: case 1: return 0; case 2: return A[0]==A[1] ? 0 : 2; }
  • 最后的话;如果是我,我会在这里重命名很多东西:result => currentConcurrentCount, count => delta, r => maxDelta, A => values
  • 我想知道我是否误解了什么,但我无法让这段代码返回正确的答案,哈哈。 “110110000”我得到 7; 4 代表“11011”;和 3 代表“1101”。

标签: c# algorithm math numbers number-theory


【解决方案1】:

您突出显示的位仅计算 当前 相邻相等值的数量,即一个值 (A[i]) 等于下一个值 (A[i+1])。然后它依次询问(第二个循环)每个值,翻转它是否会增加、减少而不是改变相邻相等值的数量;因此,如果当前值与之前的值 (A[i-1] != A[i]) 不同,则翻转将是增加 - 否则会减少;同样是它之后的那个。最终结果是预先存在的相邻相等值 (result) 的数量,加上在扫描中找到的最佳增量 (r)。

public int getMacCon(string[] A)
{
    int n = A.Length;
    int result = 0;
    // find how many adjacent values are currently in the string
    for (int i = 0; i < n - 1; i++)
    {
        // if same as the next value, that's a hit
        if (A[i] == A[i + 1])
            result = result + 1;
    }
    // worst case delta from flipping one value is that me make it
    // worse on both sides, so -2
    int r = -2;

    // now consider each value in turn
    for (int i = 0; i < n; i++)
    {
        int count = 0;
        if (i > 0) // test value before, if not the first value
        {
            if (A[i - 1] != A[i])
                count = count + 1; // before is different; flip is incr
            else
                count = count - 1; // before is same; flip is decr
        }
        if (i < n - 1) // test value after, if not the last value
        {
            if (A[i + 1] != A[i])
                count = count + 1; // after is different; flip is incr
            else
                count = count - 1; // after is same; flip is decr
        }
        // compare that to the tracking counter, and keep the best value
        r = Math.Max(r, count);
    }
    // final result is sum of pre-existing count plus the delta
    return result + r;
}

顺便说一句,优化可能是将第二个循环测试从 i &lt; n 更改为 i &lt; n &amp;&amp; r != 2 - 即一旦找到可能的最佳增量就停止(使两边都更好,+2)

【讨论】:

  • 感谢您的详细回答。但是在这里尝试理解逻辑仍然有些麻烦。假设我们的号码是“111011100110100000”。第一个循环返回 10(计算连续的 1 和 0)。下一个循环返回 2。因此最终结果变为 10+2 = 12。我看不出如何通过翻转一位来获得 12 个连续位(1 或 0)。我在这里错过了什么?
  • 哦,我想我可能误解了这个问题。这个解决方案是否意味着通过翻转数字来解决最大可能的并发数字对(1 和 0)?跨度>
  • @user119020 是的,这就是代码的作用。加2需要把010改成000或者101改成111,所以有3个东西可以翻转第4、12、13个值
【解决方案2】:

不是对您问题的直接回答(因为 Marc Gravell 的回答已经涵盖了它),但我只需要补充一下我将如何解决这个问题:

  1. 使用 RLE(运行长度编码)对您的字符串进行编码

    所以你需要b,v,n 值的数组。其中b&gt;=0 是起始位置,v={0,1} 是位值,n 是后续发生的计数。例如:

    const int _max=100; // max number of bits
    int b[_max],v[_max],n[_max],bvns=0;
    

    bvns 是数组中使用的b,v,n 的数量。您也可以改用任何动态列表/模板。

  2. 重置您的实际解决方案

    您需要更改ix 的位位置以及翻转sz 后产生的后续位的计数。将展位设置为零。

  3. 使用n=1 扫描 RLE 以查找项目

    如果发现这意味着 RLE 中的前后项目相同,则翻转将加入它们。所以计算结果大小,如果更大,则存储为实际解决方案,例如:

    for (int i=1;i<bvns-1;i++) // scann RLE except first and last item
     if (n[i]==1) // single bit found
      {
      l=n[i-1]+1+n[i+1]; // resulting size
      if (l>=sz) { sz=l; ix=b; } // update solution
      }
    
  4. 测试放大单个序列是否变大

    简单地说:

    for (int i=0;i<bvns;i++) // scann RLE
     if (n[i]>=sz) // result is bigger?
      {
      sz=l; ix=b-1; // update solution
      if (ix<0) ix=b+n[i];
      }
    

【讨论】:

  • 这似乎比现有代码复杂得多......只是说'
  • @MarcGravell 只能通过文字...生成的代码更小更快,但需要更多内存...
  • 如果它提高了性能,我会感到非常惊讶;最终[问题中显示的代码]只是做一个简单的 O(N) 扫描。如果我们想避免一组额外的可能的内存负载,现有的代码可以简化为一个循环。如果(假设)我们发现自己有一个巨大的字符串要处理,那么现有的代码也可以简单地并行化。
  • @MarcGravell 同意 ... 也不需要 b[],v[] 并且使用线性前向扫描只访问一次字符串。最后一个条件也可以移到循环外...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多