替换子串得到平衡字符串

标签: 字符串 滑动窗口

难度: Medium

有一个只含有 '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"。

提示:

  • 1 <= s.length <= 10^5
  • s.length 是 4 的倍数
  • s 中只含有 'Q', 'W', 'E''R' 四种字符

Submission

运行时间: 94 ms

内存: 16.4 MB

class Solution:
    def balancedString(self, s: str) -> int:
        n = len(s)
        cnt = Counter(s)
        avg = n//4
        d = {}
        for i in "QWER":
            if cnt[i] > avg:
                d[i] = cnt[i] - avg
        if not d:
            return 0
        l = r = 0
        res = n
        # print(d)
        matched = len(d)
        for r in range(n):
            if s[r] in d:
                d[s[r]]-=1
                if d[s[r]]==0:
                    matched-=1
            while matched==0:
                res = min(res, r-l+1)
                if s[l] in d:
                    d[s[l]]+=1
                    if d[s[l]]>0:
                        matched+=1
                l+=1
        return res

Explain

本题解使用滑动窗口(sliding window)策略来找到使字符串平衡的最小子串长度。首先,计算每个字符'Q', 'W', 'E', 'R'的出现次数,并确定每个字符应有的平均出现次数(n/4)。接着,创建一个字典d来记录每个字符超出平均值的数量。若所有字符的数量都不超过平均值,则字符串已经平衡,直接返回0。使用双指针技术(左指针l和右指针r),扩展右指针以包含足够的超额字符,然后逐步移动左指针以缩小窗口,直到窗口内的字符不再能覆盖所有超额字符。过程中记录并更新最小窗口长度。

时间复杂度: O(n)

空间复杂度: O(1)

class Solution:
    def balancedString(self, s: str) -> int:
        n = len(s)
        cnt = Counter(s)
        avg = n//4
        d = {}
        for i in 'QWER':
            if cnt[i] > avg:
                d[i] = cnt[i] - avg
        if not d:
            return 0
        l = r = 0
        res = n
        matched = len(d)
        for r in range(n):
            if s[r] in d:
                d[s[r]]-=1
                if d[s[r]]==0:
                    matched-=1
            while matched==0:
                res = min(res, r-l+1)
                if s[l] in d:
                    d[s[l]]+=1
                    if d[s[l]]>0:
                        matched+=1
                l+=1
        return res

Explore

在这个问题中,目的是找到最小的子串,使得整个字符串中每个字符的数量不超过平均值。记录超过平均值的字符是因为我们需要通过替换这些字符来减少它们的数量,从而达到平衡。低于平均值的字符不需要在这个过程中被减少,所以没有必要记录它们。关键在于缩减超额部分,而不是增加不足部分。

当s[l]不在字典d中,表示这个字符的数量不需要减少,因此移动左指针l来缩小窗口是适当的操作。这意味着我们可以尝试减小窗口的大小而不影响超额字符的匹配状态。这种操作有助于我们找到可能的最小窗口,因此是寻找最优解的重要步骤。

双指针策略通常涉及两个指针以不同的速度或方向在数组或列表上移动以解决问题,比如在排序数组中找到特定和的两个数字。而滑动窗口是双指针策略的一种,特别适用于需要在字符串或数组中查找符合某些条件的连续子区间的问题。在滑动窗口方法中,两个指针代表窗口的边界,共同向前移动以探索所有可能的窗口。尽管滑动窗口是基于双指针的,但它更侧重于窗口的大小和窗内元素的处理。