leetcode 1573. 分割字符串的方案数 题解

题目

  • 给你一个二进制串 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和下标31;第二份,一定包含下标7和下标91;第三份一定包含下标12和下标131。这些一定是固定的。
  • 而三份被平均分之后的1之间的0,就相当于”墙头草“,可以摇摆到任意一边,就此造成了不同的组合。
    • 比如,示例四。下标3和下标7之间的0,就是随意摇摆的。分隔符可以在34之间开始,一直移动到67之间。分隔符一共可以有4个位置放置,也就是可以有4种组合。看规律可知,也就是第一段末尾1和第二段开头1之间下标的差(7-3)。第二段和第三段也是如此。
  • 因此,可以推断出,只要找到每段需要有多少个1(1的总数/3),再找到每段1的开头和末尾,计算段与段之间间隔的下标差。就可以知道两段之间,有多少种可能。
  • 而需要计算三段总共有多少种组合的可能,那就将一二段的组合数与二三段的组合数相乘即可。

代码实现

class Solution {
  public int numWays(String s) {
    List<Integer> location = new ArrayList<>();  // 用来记录所有包含`1`的下标
    long count = 0;  // 1的个数
    // 遍历所有位置,找出所有1的下标以及个数总和
    for (int i = 0; i < s.length(); i++) {
      if ('1' == (s.charAt(i))) {
        location.add(i);
        count++;
      }
    }
    // 情况一:不是3的倍数,直接返回0
    if (count % 3 != 0) return 0;
    // 情况二:如果全是0,那就返回组合的结果
    if (count == 0) {
      long n = s.length();
      return (int) (((n - 1) * (n - 2) / 2) % 1000000007);
    }
    // 情况三:
    int middle3 = (int) (count / 3);  // 返回每段需要包含1的个数
    long firstSpace = (location.get(middle3) - location.get(middle3 - 1));  // 第一段和第二段之间,0的个数
    long secondSpace = (location.get(2 * middle3) - location.get(2 * middle3 - 1));// 第二段和第三段之间,0的个数
    return (int) ((firstSpace * secondSpace) % 1000000007L);  // 组合结果,返回
  }

  public static void main(String[] args) {
    int res = new Solution().numWays("10101");
    System.out.println(res);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值