Problem: 1653. 使字符串平衡的最少删除次数
文章目录
- [1. 整体思路](#1. 整体思路)
- [2. 完整代码](#2. 完整代码)
- [3. 时空复杂度](#3. 时空复杂度)
-
-
- [时间复杂度: O ( N ) O(N) O(N)](#时间复杂度: O ( N ) O(N) O(N))
- [空间复杂度: O ( 1 ) O(1) O(1)](#空间复杂度: O ( 1 ) O(1) O(1))
-
1. 整体思路
核心问题
删除最少字符使得字符串中所有的 'a' 都在 'b' 之前。
算法逻辑
使用动态规划,并利用滚动变量进行状态压缩。
- 状态
f:代表遍历到当前位置时,使当前前缀字符串平衡所需的最少删除次数 (相当于之前的dp[i])。 - 变量
cntB:记录当前遍历到的 'b' 的总数量。 - 状态转移 :
- 遍历字符串
s。 - 如果当前字符是 'b' :
- 这个 'b' 可以直接追加在任何已经平衡的字符串后面,不会破坏平衡性。
- 因此,最小删除次数
f保持不变。 - 更新
cntB(加 1)。
- 如果当前字符是 'a' :
- 此时有两个选择来维持平衡:
- 删除当前的 'a' :代价是之前的最小删除次数
f加上这次删除的代价 1。即f + 1。 - 保留当前的 'a' :这意味着这个 'a' 必须放在所有的 'b' 之前。为了做到这一点,我们必须删除之前出现的所有 'b' 。代价是
cntB。
- 删除当前的 'a' :代价是之前的最小删除次数
- 更新
f为两者的最小值:f = Math.min(f + 1, cntB)。
- 此时有两个选择来维持平衡:
- 遍历字符串
2. 完整代码
java
class Solution {
public int minimumDeletions(String s) {
// f 记录当前位置之前的最小删除次数
// 初始化为 0,因为空前缀是平衡的
int f = 0;
// cntB 记录当前遍历过的 'b' 的数量
int cntB = 0;
// 遍历字符串中的每个字符
// toCharArray() 会创建一个新的字符数组,如果为了极致空间可以换成 charAt()
for (char c : s.toCharArray()) {
if (c == 'b') {
// 如果当前是 'b',它不会破坏平衡性 (因为我们在向右构建,b 总是在右边)
// 只需要记录 b 的数量增加
cntB++;
} else {
// 如果当前是 'a',它可能会破坏平衡性 (因为它出现在了之前的 b 后面)
// 我们有两种修复方案:
// 1. 删除这个 'a' (代价 f + 1)
// 2. 保留这个 'a',但删除前面所有的 'b' (代价 cntB)
// 取较小者作为新的最小删除次数
f = Math.min(f + 1, cntB);
}
}
// 返回最终的最小删除次数
return f;
}
}
3. 时空复杂度
假设字符串 s 的长度为 N N N。
时间复杂度: O ( N ) O(N) O(N)
- 计算依据 :
- 代码包含一次线性遍历,访问每个字符一次。
- 循环内部操作为 O ( 1 ) O(1) O(1)。
- 结论 : O ( N ) O(N) O(N)。
空间复杂度: O ( 1 ) O(1) O(1)
- 计算依据 :
- 只使用了两个整型变量
f和cntB。 - 虽然使用了
s.toCharArray()产生了 O ( N ) O(N) O(N) 的临时空间,但如果在面试中可以直接用charAt遍历,算法逻辑本身是 O ( 1 ) O(1) O(1) 的。
- 只使用了两个整型变量
- 结论 : O ( 1 ) O(1) O(1)。