Python 练习 字符串 [统计]: LeetCode 696 计数二进制子串
给定一个字符串 s
,统计并返回具有相同数量 0
和 1
的非空(连续)子字符串的数量,并且这些子字符串中的所有 0
和所有 1
都是成组连续的。
重复出现(不同位置)的子串也要统计它们出现的次数。
示例 1:
输入:s = "00110011"
输出:6
解释:6 个子串满足具有相同数量的连续 1
和 0
:"0011"
、"01"
、"1100"
、"10"
、"0011"
和 "01"
。
注意,一些重复出现的子串(不同位置)要统计它们出现的次数。
另外,"00110011"
不是有效的子串,因为所有的 0
(还有 1
)没有组合在一起。
示例 2:
输入:s = "10101"
输出:4
解释:有 4 个子串:"10"
、"01"
、"10"
、"01"
,具有相同数量的连续 1
和 0
。
提示:
1 <= s.length <= 1e5
s[i]
为 '0'
或 '1'
题解一
两层遍历
要寻找字符串中的子串,可以直接采用暴力求解的方式,两层遍历得到结果。但是这种算法时间复杂度超过了题目要求
class Solution:
def countBinarySubstrings(self, s: str) -> int:
if len(s) < 2:
return 0
res = 0
maxLength = 2 * int(len(s)/2)
for i in range(2, maxLength+2, 2):
for j in range(len(s)-i+1):
piece = s[j:j+i]
pieceNeeded_0 = "0" * int(i/2) + "1" * int(i/2)
pieceNeeded_1 = "1" * int(i/2) + "0" * int(i/2)
if piece == pieceNeeded_0 or piece == pieceNeeded_1:
res += 1
return res
题解二
两次遍历
这一题可以在暴力求解的基础上简化思路。可以将字符串 s
按照 0
和 1
的连续段分组存在数组中。这里数组中两个相邻的数一定代表的是两种不同的字符,数组中两个相邻的数值 u
和 v
代表的信息是能组成的满足条件的子串数目为 min{u,v}
,即一对相邻的数字对答案的贡献。只要遍历所有相邻的数对,求它们的贡献总和即可得到结果
这种算法简化了暴力求解的思路,只需要两次遍历完成暴力求解中两层遍历的计算
class Solution:
def countBinarySubstrings(self, s: str) -> int:
statList = []
stat = 1
for i in range(len(s)-1):
if s[i+1] == s[i]:
stat += 1
else:
statList.append(stat)
stat = 1
statList.append(stat)
res = sum([min(statList[i], statList[i+1]) for i in range(len(statList)-1)])
return res
小结:
这个问题的解决思路主要基于观察相邻字符的变化以及计数。在一次遍历字符串的过程中,就能够有效地计算出符合条件的子字符串的数量,同时避免了不必要的重复计算