假设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 <= j
,A[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 = '0101
和i = 3
(字符串的结尾)。目前,count = count + F[0]
为4,因为count = 2
和F[0] = 2
。我们是否重复计算?我们应该根据(*)?
count
仅增加1
非常感谢。
答案 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
)