夯实算法-21.计数二进制子串

208 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情

题目:LeetCode

给定一个字符串 s,统计并返回具有相同数量 01 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是成组连续的。

重复出现(不同位置)的子串也要统计它们出现的次数。

示例 1:

输入:s = "00110011"
输出:6
解释:6 个子串满足具有相同数量的连续 10"0011""01""1100""10""0011""01" 。
注意,一些重复出现的子串(不同位置)要统计它们出现的次数。
另外,"00110011" 不是有效的子串,因为所有的 0(还有 1 )没有组合在一起。

示例 2:

输入: s = "10101"
输出: 4
解释: 有 4 个子串:"10""01""10""01" ,具有相同数量的连续 10

提示:

  • 1 <= s.length <= 105
  • s[i] 为 '0' 或 '1'

解题思路

从题干中获取信息:具有相同数量且连续的1和0,重复出现只要位置不同也要计入次数。
基于上面分析有如下一种思路:
可以将目标字符串 s 根据 0 和 1 的连续段分组,用数组 counts 暂存,例如 s=00111011,可以得到这样的 counts 数组:counts={2,3,1,2}。

那所以 counts 数组中相邻的两个元素必定代表的是两种不同的字符'1'或'0'。假设counts 数组中两个相邻的数字为 a 或者 b,它们就表示连续的 a 个 '0' 和 b 个 '1',或者 a 个 '1' 和 b 个 '0'。它们能组成的满足条件的子串数目为min{a,b},即一对相邻元素包含的满足条件子串。

只要通过遍历所有相邻的数对,求得总和值,即为所求二进制子串数。

代码实现

public int countBinarySubstrings(String s) {
    List<Integer> counts = new ArrayList<Integer>();  // 记录0或1连续段包含字符的个数
    int ptr = 0, n = s.length(); // 定义遍历的起始位置
    while (ptr < n) {
        char c = s.charAt(ptr);  // 逐个取出字符
        int count = 0;  // 计数目标字符出现的次数
        while (ptr < n && s.charAt(ptr) == c) {
            ++ptr;
            ++count;
        }
        counts.add(count);   // 将数值加入到临时数组counts中
    }
    int ans = 0;
    for (int i = 1; i < counts.size(); ++i) {    // 遍历临时数组counts
        ans += Math.min(counts.get(i), counts.get(i - 1)); // 满足条件的子串数取较小值
    }
    return ans;
}

运行结果

Snipaste_2022-08-31_08-35-43.png

复杂度分析

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)

掘金(JUEJIN)  一起分享知识, Keep Learning!