问题描述
假设 S 是一个只包含 0 和 1 的字符串,我想统计 S 的非空子串的个数,其中 0 的个数小于 1 的个数。
使用下面给出的蛮力方法,我们可以有一个算法在 O(n^2) 中解决这个问题:
moreOnes(s):
count = 0
n = s.len()
for i = 1 to n
one = 0
zero = 0
for j = i to n
if s[i] == '1'
one++
else
zero++
if one > zero
count++
return count
但是我们能不能有一个算法的时间复杂度比 O(n*logn) 或 O(n) 更好,空间复杂度可以从 O(1) 到 O(n)?
解决方法
考虑一个数组 A[i],它包含范围 1..i 中的 1 数减去范围 1..i. 中的零数
有了这个数组,通过计算 A[j]-A[i-1],现在只需要 O(1) 时间来计算来自 i..j 的子字符串中 1 的数量减去 0 的数量。>
对于给定的端点 j,您希望找到所有起始点 iA[j]-A[i-1]>0。等效地,您想知道有多少 A[i-1] 的值小于 A[j]。
这可以通过 Fenwick trees 解决:
- 循环 j
- 在 Fenwick 树中的位置
A[j]
添加 1 - 从 Fenwick 树中查找累积值,范围高达
A[j]-1
- 这是以 j 结尾并满足所需的 1 多于 0 的属性的子串的数量。
Fenwick 树需要 O(n) 空间和 O(nlogn) 时间,因为每次查找都是 O(logn)。
(请注意,A[j] 可能会变为负数,而 Fenwick 树通常处理正数数据。您可以通过向 A[j] 添加常数偏移量 n 来解决此问题,以便所有条目都为正数。)