题目
- 给你一个二进制串 s (一个只包含 0 和 1 的字符串),我们可以将 s 分割成 3 个 非空 字符串 s1, s2, s3 (s1 + s2 + s3 = s)。
请你返回分割 s 的方案数,满足 s1,s2 和 s3 中字符 ‘1’ 的数目相同。
由于答案可能很大,请将它对 10^9 + 7 取余后返回。
官方题目 - 示例一:
输入:s = "10101"
输出:4
解释:总共有 4 种方法将 s 分割成含有 '1' 数目相同的三个子字符串。
"1|010|1"
"1|01|01"
"10|10|1"
"10|1|01"
- 示例二:
输入:s = "1001"
输出:0
- 示例三:
输入:s = "0000"
输出:3
解释:总共有 3 种分割 s 的方法。
"0|0|00"
"0|00|0"
"00|0|0"
- 示例四:
输入:s = "100100010100110"
输出:12
- 提示:
s[i] == '0' 或者 s[i] == '1'
3 <= s.length <= 10^5
解题思路
- 此题重点:分割。将字符串分割成三段,也就是需要在两个位置上确定分割符。
- 分割的条件是,
s1,s2 和 s3 中字符 '1' 的数目相同
。因此,肯定是要知道每一个1
的位置的,及1
的总数的。
情况一:
- 而看示例二可以看出,当
1
的个数不是3的倍数时,方案数为0。因此,在遍历完,数完1
的个数之后,加一个判断条件,不是3的倍数,赶紧结束。
情况二:
- 而有一种很特殊的情况,若字符串全
0
。那分隔符就可以在所有间隔之间随意选择两个。字符串长度为n,间隔一共有n-1
个,每次从间隔中选择两个。
- 用数学的式子来表示,就是
(n-1)*(n-2)/2
种可能。(高中的排列组合)
情况三:
- 分割字符串,使得
1
分布均匀,只要将所有1
等分就好,中间的0
随意。因此,所有被分割的1
的位置,实际上是固定的。
- 比如说,示例四
"100100010100110"
,一共有6个1
,肯定要平均分,每份2个1
。因此,第一份一定要包含下标0
和下标3
的1
;第二份,一定包含下标7
和下标9
的1
;第三份一定包含下标12
和下标13
的1
。这些一定是固定的。
- 而三份被平均分之后的
1
之间的0
,就相当于”墙头草“,可以摇摆到任意一边,就此造成了不同的组合。
- 比如,示例四。下标
3
和下标7
之间的0
,就是随意摇摆的。分隔符可以在3
和4
之间开始,一直移动到6
和7
之间。分隔符一共可以有4个位置放置,也就是可以有4种组合。看规律可知,也就是第一段末尾1
和第二段开头1
之间下标的差(7-3
)。第二段和第三段也是如此。
- 因此,可以推断出,只要找到每段需要有多少个1(
1
的总数/3),再找到每段1
的开头和末尾,计算段与段之间间隔的下标差。就可以知道两段之间,有多少种可能。 - 而需要计算三段总共有多少种组合的可能,那就将一二段的组合数与二三段的组合数相乘即可。
代码实现
class Solution {
public int numWays(String s) {
List<Integer> location = new ArrayList<>();
long count = 0;
for (int i = 0; i < s.length(); i++) {
if ('1' == (s.charAt(i))) {
location.add(i);
count++;
}
}
if (count % 3 != 0) return 0;
if (count == 0) {
long n = s.length();
return (int) (((n - 1) * (n - 2) / 2) % 1000000007);
}
int middle3 = (int) (count / 3);
long firstSpace = (location.get(middle3) - location.get(middle3 - 1));
long secondSpace = (location.get(2 * middle3) - location.get(2 * middle3 - 1));
return (int) ((firstSpace * secondSpace) % 1000000007L);
}
public static void main(String[] args) {
int res = new Solution().numWays("10101");
System.out.println(res);
}
}