替换子串得到平衡字符串
难度:中等
有一个只含有 'Q'
, 'W'
, 'E'
, 'R'
四种字符,且长度为 n
的字符串。
假如在该字符串中,这四个字符都恰好出现 n/4
次,那么它就是一个「平衡字符串」。
给你一个这样的字符串 s
,请通过「替换一个子串」的方式,使原字符串 s
变成一个「平衡字符串」。
你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。
请返回待替换子串的最小可能长度。
如果原字符串自身就是一个平衡字符串,则返回 0
。
示例 1:
输入:s = "QWER"
输出:0
解释:s 已经是平衡的了。
示例 2:
输入:s = "QQWE"
输出:1
解释:我们需要把一个 'Q' 替换成 'R',这样得到的 "RQWE" (或 "QRWE") 是平衡的。
示例 3:
输入:s = "QQQW"
输出:2
解释:我们可以把前面的 "QQ" 替换成 "ER"。
示例 4:
输入:s = "QQQQ"
输出:3
解释:我们可以替换后 3 个 'Q',使 s = "QWER"。
双指针 + 哈希表
思路:
通过题目分析,不难得出结论,我们只要确保删除某个子串后,剩余的字符串统计起来,'Q'
, 'W'
, 'E'
, 'R'
出现的次数小于等于 n/4
次即可,其中 n
为字符串的长度。
我们可以使用暴力算法,穷举出所有可能的子串,并统计不包含子串中字符的个数,取子串最短的结果输出,但是时间复杂度为 O ( n ) O(n) O(n),需要进行优化。
经过对上述思路的梳理,我们可以使用头尾指针来表明一个子串的开始和结尾,用哈希表来统计字符出现的次数。
- 经过上述的思路,我们不难推出,需要申明一个哈希表 w o r d _ d i c t s word\_dicts word_dicts,申明两个指针 l l l 和 r r r,起初,我们定义所有的字符串都为需要替换的子串,故 l = 0 l=0 l=0, r = n r=n r=n。
- 首先,我们需要计算出第一个不满足要求的字符所在的位置,将其左指标
l
l
l 更新于此,即遍历字符串,对字符串进行统计,查询出第一个字符大于
n/4
的字符串的位置。 - 其次,我们要对右节点进行更新,更新步骤如下:
- 先对右节点进行收敛,外层循环判断:
- 如果右节点小于左节点,则循环结束。
- 每次都将右节点的字符进行统计,如果字符数量大于
n/4
,则循环结束。 - 每一次循环没有结束,右节点都要减一。
- 右节点收敛后,即可得到当前左节点在当前位置时,和右节点能出现的最优替换子串, r − l + 1 r - l + 1 r−l+1 即为当前替换子串长度,我们对其进行记录,若该长度最短,则替换返回结果。
- 左节点进行减一,将字符统计哈希表减去当前字符数量,再次重复第三步右节点更新,计算左节点减一后的最优替换子串,对最终替换长度进行更新,直到左节点位置为 − 1 -1 −1 结束循环。
- 先对右节点进行收敛,外层循环判断:
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 为 s s s 的长度。
- 空间复杂度: O ( c ) O(c) O(c), 其中 c c c 为 4 的长度。
class Solution:
def balancedString(self, s: str) -> int:
max_size, res = len(s) // 4, inf
l, r = 0, len(s) - 1
word_dicts = {i:0 for i in "QWER"}
# 确定左边边界
for i in range(len(s)):
if word_dicts[s[i]] == max_size:
l = i
break
word_dicts[s[i]] += 1
else:
return 0
# 右边边界查询
while l != -1:
# 尝试缩小窗口
while l < r:
if word_dicts[s[r]] == max_size:
break
word_dicts[s[r]] += 1
r -= 1
l -= 1
word_dicts[s[l]] -= 1
# 更新最短长度
res = min(res, r - l)
return res
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/replace-the-substring-for-balanced-string