问题描述
解决思路与代码实现
这道题初看起来,暴力求解的规模似乎也不是很大,只需要从头至尾对字符串做一次遍历即可,于是笔者很自然地尝试了一下暴力求解:
class Solution(object):
def numSplits(self, s):
"""
:type s: str
:rtype: int
"""
num_good = 0
for i in range(1, len(s)):
if len(set(s[: i])) == len(set(s[i:])):
num_good += 1
return num_good
然而,提交后运行超时,超时的算例是一个长度为 24485 的超长字符串(由于字符串过长,此处暂不列出)。
反思前面暴力求解的代码,可以看出,每次循环的 set 函数,会消耗很多时间,而在对字符串从左向右的遍历过程中,左右两侧子串的 set 的值一定程度是可以复用的。
因此,可以参考动态规划的思想,定义一个集合用来记录左侧子串出现过的字符,定义一个字典以记录右侧子串剩余的字符及出现次数,从左向右遍历,每次循环对集合和字典的内容进行微调即可。
修改后的代码如下:
class Solution(object):
def numSplits(self, s):
"""
:type s: str
:rtype: int
"""
# 左侧子串的字符集合;右侧子串的字符字典
set_left, dict_right = set(), {}
for c in s:
if c in dict_right.keys():
dict_right[c] += 1
else:
dict_right[c] = 1
num_good = 0
for c in s:
if c not in set_left: # 若当前字符不在左侧子串的字符集合中,则向集合中加入该字符
set_left.add(c)
dict_right[c] -= 1 # 在右侧子串的字符字典中,将该字符的出现次数减 1
if not dict_right[c]: # 若更新后的出现次数为 0,则在字典的 key 中删除该字符
dict_right.pop(c)
if len(set_left) == len(dict_right):
num_good += 1
return num_good
运行效果