在二进制字符串中查找最长的正子串

时间:2016-10-08 08:13:15

标签: algorithm math data-structures

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

  • 尽可能长
  • 具有总正和> 0

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

例如,对于100110001010001以上的字符串,它将是:[10011]000[101]000[1]

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

不幸的是我不知道怎么办,不能用蛮力的方式来做。有什么想法吗?

2 个答案:

答案 0 :(得分:0)

动态编程。

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

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

我们可以快速消除,因为答案必须是最大值,或者从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 */

这是未经测试的,最后的检查有点繁琐,我可能会 在某个地方犯了错误,但算法应该或多或少地起作用 如上所述。

答案 1 :(得分:0)

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

我将概述(3)的方法,因为它很容易转换为(1)或(2)。在O(n log n)时间和O(n)额外空间中查找每个索引中包含多于零的最长左延伸,可以在O(n)时间内完成(对于O(n^2)时间内最长的有效子字符串,请参见此处: Finding the longest non-negative sub array)。通过该预处理,可以在稍微优化O(n)时间和s[i]额外空间的动态编程中找到有效,非重叠子串的最长组合。

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

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

每个索引左侧最长的有效子字符串扩展到最左边的索引,前缀总和较小,可以在树中轻松查找。

要获得最长的组合,请i代表索引f(i)之前的最长组合。然后j等于可以将f(j-1)添加到SELECT a.originalID, b.part from #1 a join #2 b on a.area = b.area where not exists (select * from #3 c where c.originalID = a.originalID and c.part = b.part) 的每个有效左扩展的最大长度。