Medium
题目描述
给你一个只包含字符 ‘a’ 和 ‘b’ 的字符串 s。
你可以重复移除任何满足 ‘a’ 字符数量等于 ‘b’ 字符数量的子字符串。每次移除后,字符串的剩余部分会无缝连接在一起。
返回执行任意次此类操作后字符串的最小可能长度。
示例 1:
输入:s = "aabbab"
输出:0
解释:子字符串 "aabbab" 有三个 'a' 和三个 'b'。由于它们的数量相等,我们可以直接移除整个字符串。最小长度是 0。
示例 2:
输入:s = "aaaa"
输出:4
解释:"aaaa" 的每个子字符串都只包含 'a' 字符。无法移除任何子字符串,因此最小长度仍为 4。
示例 3:
输入:s = "aaabb"
输出:1
解释:首先,移除子字符串 "ab",剩下 "aab"。然后,移除新的子字符串 "ab",剩下 "a"。无法进一步移除,因此最小长度是 1。
约束条件:
1 <= s.length <= 10^5s[i]是 ‘a’ 或 ‘b’
提示:
- 最初移除最长的可能平衡子字符串
- 设 ‘a’ 的数量为
count_a,‘b’ 的数量为count_b。我们能从这些推导出最终长度吗? - 答案是
abs(count_a - count_b)
解题思路
这道题的关键在于理解平衡移除的本质。
核心思路
题目允许移除任何 ‘a’ 和 ‘b’ 数量相等的子字符串。经过分析,我们可以发现一个重要规律:无论怎样移除,最终剩余字符串的长度总是等于原字符串中 ‘a’ 和 ‘b’ 数量差的绝对值。
证明思路
每次移除都保持差值不变:每次移除的子字符串中 ‘a’ 和 ‘b’ 数量相等,因此移除操作不会改变整个字符串中 ‘a’ 和 ‘b’ 的数量差。
最终无法继续移除的情况:当字符串中只剩下一种字符(全是 ‘a’ 或全是 ‘b’)时,就无法找到平衡的子字符串进行移除了。此时剩余字符的数量正好等于原始差值。
贪心策略有效性:我们总是可以通过合适的移除顺序达到理论最小值,因为任何包含相等数量 ‘a’ 和 ‘b’ 的子字符串都可以被移除。
算法步骤
- 统计字符串中 ‘a’ 和 ‘b’ 的数量
- 返回两者数量差的绝对值
时间复杂度 O(n),空间复杂度 O(1),这是最优解法。
代码实现
class Solution {
public:
int minLengthAfterRemovals(string s) {
int count_a = 0, count_b = 0;
for (char c : s) {
if (c == 'a') {
count_a++;
} else {
count_b++;
}
}
return abs(count_a - count_b);
}
};
class Solution:
def minLengthAfterRemovals(self, s: str) -> int:
count_a = s.count('a')
count_b = s.count('b')
return abs(count_a - count_b)
public class Solution {
public int MinLengthAfterRemovals(string s) {
int count_a = 0, count_b = 0;
foreach (char c in s) {
if (c == 'a') {
count_a++;
} else {
count_b++;
}
}
return Math.Abs(count_a - count_b);
}
}
/**
* @param {string} s
* @return {number}
*/
var minLengthAfterRemovals = function(s) {
let countA = 0;
let countB = 0;
for (let char of s) {
if (char === 'a') {
countA++;
} else {
countB++;
}
}
return Math.abs(countA - countB);
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历整个字符串统计字符数量 |
| 空间复杂度 | O(1) | 只使用常数额外空间存储计数器 |