在问 · 算法题解

696. 计数二进制子串

题目描述

输入一个只由 '0' 和 '1' 组成的字符串 s。

统计满足以下条件的非空连续子串的个数:子串中 0 和 1 的数量相同,并且所有 0 挤在一起、所有 1 也挤在一起,例如 "0011"、"10"。

内容相同但位置不同的子串要分别计数。

示例 1
输入:s = "00110011"
输出:6

解释:符合条件的有 "0011"、"01"、"1100"、"10"、"0011"、"01",共 6 个。

示例 2
输入:s = "10101"
输出:4

解释:符合条件的是两个 "10" 和两个 "01"。

数据范围

  • 1 <= s.length <= 10^5
  • s[i] 为 '0' 或 '1'。

解题思路

符合要求的子串一定横跨两个相邻的“连续段”:前半是一段的末尾,后半是下一段的开头。相邻两段长度为 prev 和 cur 时,可以取 1+1、2+2、…,最多到 min(prev, cur),所以贡献 min(prev, cur) 个。用双指针划分连续段:i 指向一段开头,j 向右走到字符变化处,j - i 就是这一段的长度,然后 i = j 进入下一段。

为什么正确

处理完一段后,ans 等于完全位于 s[0..j) 内的合法子串个数,prev 是刚结束的那一段的长度。

复杂度分析

时间 O(n),j 只向右走;额外空间 O(1)。

易错点

不要枚举所有子串(O(n^2) 会超时);注意是取 min 而不是把两段长度相加,第一段没有前一段,prev 初始为 0。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def countBinarySubstrings(self, s: str) -> int:
        n = len(s)
        ans = 0
        prev = 0
        i = 0
        while i < n:
            j = i
            while j < n and s[j] == s[i]:
                j += 1
            cur = j - i
            ans += min(prev, cur)
            prev = cur
            i = j
        return ans

载入编辑器,自己跑一遍 →

双指针 专题的其他题目

查看 双指针 全部题目 →