目录
将二进制字符串分割成3部分,保证每部分1的个数相同 — 题解与分析
将二进制字符串分割成3部分,保证每部分1的个数相同 — 题解与分析
题目描述
给定一个只包含 '0'
和 '1'
的二进制字符串 s
,要求将其分割成三个 非空 字符串 s1, s2, s3
(满足 s1 + s2 + s3 = s
),使得这三个子串中 '1'
的数量都相等。
请返回满足条件的分割方案数。由于答案可能很大,需对 10^9 + 7
取模。
题目背景与意义
这个题目考察对字符串的计数、分割和组合的理解,特别是如何高效处理字符串中某种字符的分布以及分割点的选择。此类问题在字符串算法、动态规划、数学计数等领域都非常典型,尤其是在对“满足某种均分条件的多段划分方案”求解时常用技巧。
解题分析
1. 问题关键:每个子串中 '1'
的数量相等
- 设字符串长度为
n
。 - 统计字符串中
'1'
的总数为count_1
。 - 如果
count_1
不能被3整除,显然无法均分成三个部分,返回0。 - 如果
count_1
是0,则字符串中全是0
,此时只需统计分割点的组合数。 - 如果
count_1
是3的倍数,设target = count_1 / 3
,每个子串中应包含target
个'1'
。
2. 特殊情况:count_1 == 0
字符串全为0,三部分的 '1'
数都为0,满足条件。
分割方式就是在 n-1
个位置中选两个分割点。
组合数为:
3. 一般情况:count_1
可整除3
- 找到字符串中所有
'1'
的位置索引,记为ones_positions
。 - 设目标每部分
'1'
数为target
。 - 则:
-
- 第
target
个'1'
的索引是ones_positions[target - 1]
- 第
target + 1
个'1'
的索引是ones_positions[target]
- 第
2 * target
个'1'
的索引是ones_positions[2 * target - 1]
- 第
2 * target + 1
个'1'
的索引是ones_positions[2 * target]
- 第
- 第一个分割点可以放在第
target
个'1'
后的所有位置(在ones_positions[target - 1]
到ones_positions[target]
之间的位置) - 第二个分割点可以放在第
2 * target
个'1'
后的所有位置(在ones_positions[2 * target - 1]
到ones_positions[2 * target]
之间的位置)
方案数为:
(第一个分割点的可选位置数) × (第二个分割点的可选位置数) = (ones_positions[target] − ones_positions [ target − 1 ] ) × ( ones_positions [ 2 ∗ target ] − ones_positions [ 2 ∗ target − 1 ] )
解题方法
根据上面的分析,代码实现如下:
class Solution:
def numWays(self, s: str) -> int:
MOD = 10**9 + 7
n = len(s)
count_1 = s.count('1')
# 全为0的情况
if count_1 == 0:
return ((n - 1) * (n - 2) // 2) % MOD
# 不能被3整除的情况
if count_1 % 3 != 0:
return 0
target = count_1 // 3
ones_positions = [i for i, ch in enumerate(s) if ch == '1']
first_cut_start = ones_positions[target - 1]
first_cut_end = ones_positions[target]
second_cut_start = ones_positions[2 * target - 1]
second_cut_end = ones_positions[2 * target]
ways = (first_cut_end - first_cut_start) * (second_cut_end - second_cut_start)
return ways % MOD
代码分析与复杂度
- 统计
'1'
数量时间复杂度为 O(n)。 - 收集所有
'1'
的位置时间复杂度为 O(n)。 - 计算方案数为 O(1)。
- 总体时间复杂度:O(n),非常高效。
- 空间复杂度:O(k),k 为字符串中
'1'
的个数。
示例说明
假设输入:
s = "10101"
- 总的
'1'
个数count_1 = 3
,能被3整除,target = 1
。 '1'
的位置为[0, 2, 4]
。- 第
target=1
个'1'
位置:ones_positions[0] = 0
- 第
target+1=2
个'1'
位置:ones_positions[1] = 2
- 第
2*target=2
个'1'
位置:ones_positions[1] = 2
- 第
2*target+1=3
个'1'
位置:ones_positions[2] = 4
计算:
- 第一个切分区间长度:
2 - 0 = 2
(即切分点可以在索引1或2) - 第二个切分区间长度:
4 - 2 = 2
(切分点可以在索引3或4)
方案数为 2 * 2 = 4
。
具体分割方案为:
s1 = s[0:1] = "1"
,s2 = s[1:3] = "01"
,s3 = s[3:] = "01"
s1 = s[0:1] = "1"
,s2 = s[1:4] = "010"
,s3 = s[4:] = "1"
s1 = s[0:2] = "10"
,s2 = s[2:3] = "1"
,s3 = s[3:] = "01"
s1 = s[0:2] = "10"
,s2 = s[2:4] = "10"
,s3 = s[4:] = "1"
均满足每部分 '1'
个数相同。
小结与比较
- 简单暴力方法:枚举所有分割点,统计每段
'1'
的数量,时间复杂度高达 O(n²),不可行。 - 前缀和方法:利用前缀和快速统计每段
'1'
数量,复杂度虽然降低,但仍然高。 - 本题最优方案:通过统计
'1'
的位置,利用数学规律快速计算方案数,时间复杂度 O(n),空间复杂度 O(k)。
该方法既简洁又高效,是处理均分字符问题的典范。
结语
通过对字符串中 '1'
分布的观察与分析,结合数学组合的思想,我们能够快速计算满足均分条件的分割方案数。此题不仅锻炼了字符串处理能力,更体现了用数学思想简化问题的重要性。