【问题标题】:Find longest positive substrings in binary string在二进制字符串中查找最长的正子串
【发布时间】:2016-10-08 08:13:14
【问题描述】:

假设我有一个类似100110001010001 的字符串。我想找到这样的子字符串:

  • 尽可能长
  • 总和>0

所以最长的子串,1 比 0 多

例如,100110001010001 上面的字符串将是:[10011]000[101]000[1]

实际上找到它们的总长度是令人满意的,在这种情况下:9。

不幸的是,我不知道如何不以蛮力的方式完成。请问有什么想法吗?

【问题讨论】:

  • 更正我,但符合您条件的最长子序列 100110001010001 100110001010001。
  • @Robert,刚刚编辑过。子串,你是对的。
  • 找到一个好的算法总是从彻底分析你的需求开始,这样你就能很好地理解它们。正如您所说,@Robert 仍然是正确的。显然,这不是你想要的,但然后试着准确地表达你想要的。这将使您(和我们)获得一半的答案。
  • 我认为您应该简单地遍历整个字符串并保存“1”的第一次和最后一次出现的索引。然后,您可以简单地从最后一个索引中减去第一个索引,加 1,您就可以得到所需的最长子序列长度。
  • 相关:Longest positive sum substring(只需将0替换为实际的-1

标签: algorithm math data-structures


【解决方案1】:

正如现在发布的那样,您的问题似乎有点不清楚。 “尽可能长”的有效子字符串的总长度可能意味着不同的东西:例如,在其他选项中,它可能是(1)每个索引左侧的最长有效扩展列表(这将允许重叠在列表中),(2)最长的非重叠的左扩展组合,(3)非重叠的最长组合,有效子串(其中每个子串不一定是最长的)。

我将概述 (3) 的方法,因为它很容易转换为 (1) 或 (2)。可以在O(n log n) 时间和O(n) 额外空间中从每个索引中找到最长的左扩展,其中一个多于零的时间(对于O(n) 时间中最长的有效子字符串,请参见此处:Finding the longest non-negative sub array)。通过该预处理,可以通过动态编程在经过一定程度优化的O(n^2) 时间和O(n) 额外空间中找到最长的有效、非重叠子字符串组合。

我们首先遍历字符串,存储表示部分和的和,直到 s[i] 并包括在内,将零计数为 -1。我们将每个部分和插入到二叉树中,其中每个节点还存储值出现的索引数组,以及小于节点值的值的最左侧索引。 (如果 b 的前缀总和大于 a 的前缀总和,则从 s[a]s[b] 的子字符串的 1 多于 0。)如果值已经在树中,我们添加节点索引数组的索引。

由于我们是从左到右遍历的,只有当一个新的最小值被插入到树中时,才会更新最左边的低值索引——并且它只针对具有先前最小值的节点进行更新。这是因为任何具有较低值的节点都不需要更新;如果树中已经有任何具有较低值的节点,则任何具有较高值的​​节点都已经存储了最早插入的节点的索引。

每个索引左侧的最长有效子字符串扩展到前缀和较低的最左侧索引,可以很容易地在树中查找。

为了得到最长的组合,让f(i) 代表最长的组合直到索引i。那么f(i) 等于添加到f(j-1) 的索引j 的每个有效左扩展的最大长度。

【讨论】:

    【解决方案2】:

    动态编程。

    我们有一个字符串。如果是肯定的,这就是我们的答案。否则,我们需要修剪每一端,直到它变为正数,并找到每种修剪模式。因此,对于每个长度(N-1、N-2、N-3)等,我们都有 N 长度的可能路径(从 a 修剪,从 b 修剪),每一个都给我们一个状态。当状态变为正时,我们发现了子字符串。

    所以两个整数列表,表示如果我们完全从 a 或完全从 b 修剪会发生什么。然后回溯。如果我们从 a 中剪掉 1,我们必须从 b 中剪掉所有其余部分,如果我们从 a 中剪掉 2,我们必须从 b 中剪掉一个。有没有一个答案可以让我们变得积极?

    我们可以快速消除,因为答案必须是最大值,要么从 a 进行最大修剪,要么从 b 进行最大修剪。如果其他修剪让我们变得积极,那就是结果。

    伪代码:

    N = length(string);
    Nones = countones(string);
    Nzeros = N - Nones;
    if(Nones > Nzeroes)
       return string
    
    vector<int> cuta;
    vector<int> cutb;
    int besta = Nones - Nzeros;
    int bestb = Nones - Nzeros;
    
    cuta.push_back(besta);
    cutb.push_back(bestb);
    bestia = 0;
    bestib = 0;
    
    for(i=0;i<N;i++)
    {
       cuta.push_back( string[i] == 1 ? cuta.back() - 1 : cuta.back() +1);
       cutb.push_back( string[N-i-1] == 1 ? cutb.back() -1 : cutb.back()+1);
    
       if(cuta.back() > besta)
       {
          besta = cuta.back();
          bestia = i;
       }
      if(cutb.back() > bestb)
      {
        bestb = cutb.back();
        bestib = i;
      }
    
       // checks, is a cut from wholly from a or b going to send us positive
       if(besta == 1)
          answer = substring(string, bestia, N);
       if(bestb == 1)
          answer = substring(string, 0, N - bestib);
    
       // if not, is a combined cut from current position to the
       // the peak in the other distribution going to send us positive?
       if(Nones - Nzeros + besta + cutb.back() == 1)
      {
         answer = substring(string, bestai, N - i);
      }
      if(Nones - Nzeros + cuta.back() + bestb == 1)
      {
          answer = substring(string, i, N - bestbi);
      }
    
    }
    /*if we get here the string was all zeros and no positive substring */
    

    这是未经测试的,最后的检查有点繁琐,我可能有 在某处出错,但算法应该或多或少地工作 如前所述。

    【讨论】:

    • 我不太明白这一点。什么是a和b?原始字符串的结尾? “如果我们从 a 中修剪 1,我们必须从 b 中修剪所有其余部分,如果我们从 a 中修剪 2,我们必须从 b 中减少一个”——这听起来像你在描述一个 inner循环,围绕它的外循环是一个向下迭代长度为 N-1、N-2 等的循环。但是这样做会导致 O(n^2) 时间解决方案,当 O(n) 时间解决方案是可能。
    • 最长的正子串是整个字符串(如果是正子串),如果不是正子串,则从两端修剪字符直到变为正数。我把两端称为 a 和 b。问题是如何做到这一点以确保最大的子字符串。
    • 是的,但我还没有看到,例如,您是否有两个嵌套循环或只有一个。你介意提供一些伪代码吗?
    猜你喜欢
    • 1970-01-01
    • 2013-10-29
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-01
    相关资源
    最近更新 更多