计算具有相同0和1的子串数

时间:2016-12-30 16:17:19

标签: algorithm

假设S是仅包含0和1的字符串,S的长度不超过10^6。我想计算S的子串数,其中0的数量等于1的数量。
提供的代码如下:

#define MAXN 1000000
int count_01(std::string s) {
    int * F = new int[2 * MAXN + 1];
    memset(F, 0, sizeof(int) * (2 * MAXN + 1));
    F[0] = 1;

    int sum = 0, count = 0;
    for (int i = 0; i < (int)s.length(); ++i) {
        if (s[i] == '0')
            --sum;
        else
            ++sum;
        if (sum >= 0) {
            count += F[sum];
            ++F[sum];
        }
        else {
            count += F[MAXN + abs(sum)];
            ++F[MAXN + abs(sum)];
        }
    }
    return count;
}

如果我理解正确,当我们遍历字符串时,它依赖于0和1之间的差异,并添加count。特别是,对于i <= jA[i,j]B[i,j]分别是[i,j]中0和1的数字。然后是

  

A [i,j] = A [1,j] - A [1,i - 1],

     

B [i,j] = B [1,j] - B [1,i - 1],

[i, j]满足所需条件A[i, j] = B[i, j],我们必须

  

A [1,j] - B [1,j] = A [1,i - 1] - B [1,i - 1](*)。

这意味着我们只查看0和1的数字之间的差异,并计算此数字出现的次数。我的推理是否正确?

我不明白count是如何运作的。例如,假设S = '0101i = 3(字符串的结尾)。目前,count = count + F[0]为4,因为count = 2F[0] = 2。我们是否重复计算?我们应该根据(*)?

count仅增加1

非常感谢。

2 个答案:

答案 0 :(得分:1)

您的推理是正确的,但更简单的说法是,如果从 i j 的子字符串具有相同数量的0和1,则累积 i 的差异与 j 的累积差异相同。如果您更喜欢视觉类比,可以将1视为一步,将0视为一步;点 i 处的累积差异是 i 点的高度,具有相等计数的子串是端点处于相同高度的线。

遍历中任何一点的<[F> <总和]是精确高度 sum 的先前点数,即当前高度。所以它是当前点平衡分段结束的起点数;这些中的每一个对应于在当前点结束的平衡子串。由于我们只访问过每个点一次,因此之前没有计算过这些字符串,也没有一个会再次计算。

答案 1 :(得分:1)

F []是具有范围[-MAXN..MAXN]的虚拟折叠阵列,包含累积差异的量 - 即到目前为止已经满足每个可能的差值的次数。

对于每个新差异sum(我们停留在子字符串的末尾),我们必须将F[sum]添加到count,因为F[sum]可能有效的子字符串开始位置。

对于您的示例:有两个起始位置,零差异(0和2),这就是为什么最后一步添加两个变体(从4个可能:01, 10, 01, 0101