LeetCode 中国,https://leetcode-cn.com/contest/biweekly-contest-39/problems/minimum-deletions-to-make-string-balanced/。
给你一个字符串 s
,它仅包含字符 'a'
和 'b'
。
你可以删除 s
中任意数目的字符,使得 s
平衡 。我们称 s
平衡的 当不存在下标对 (i,j)
满足 i < j
且 s[i] = 'b'
同时 s[j]= 'a'
。
请你返回使 s
平衡 的 最少 删除次数。
输入:s = "aababbab"
输出:2
解释:你可以选择以下任意一种方案:
下标从 0 开始,删除第 2 和第 6 个字符("aababbab" -> "aaabbb"),
下标从 0 开始,删除第 3 和第 6 个字符("aababbab" -> "aabbbb")。
输入:s = "bbaaaaabb"
输出:2
解释:唯一的最优解是删除最前面两个字符。
1 <= s.length <= 105
s[i]
要么是 'a'
要么是 'b'
。LeetCode 官方将本题难度定为中等。通过阅读题目,字符串就会出现 a 和 b 两种字符。我们发现字符 b 是关键,因为后面的字符可能导致新字符串变成不平衡。因此,出现字符 b 后,需要特别操作。一般来说,这个操作就是记录 b 的个数。下面我们来分析一下样例数据。
1、下标为零的字符 s[0]='a'。对应的字符串为 "aa",符合平衡字符串,所以不需要处理;
2、下标为一的字符 s[1]='a'。对应的字符串为 "aa",符合平衡字符串,所以不需要处理;
3、下标为二的字符 s[2]='b'。对应的字符串为 "aab",符合平衡字符串,所以不需要处理。但是如果下一个字符为 a,那么就不是平衡字符串了,这样我们只要删除一个 b 就可以变成平衡字符串。因此将 b 计数,cnt=1;
4、下标为三的字符 s[3]='a'。对应的字符串为 "aaba",不符合平衡字符串,我们删除 s[2],将字符串变成 "aaa";或者删除 s[3],将字符串变成 “aab”。考虑到后面还有字符,因此将字符串变成 “aaa” 更符合要求,这样我们 cnt 减一变成 0;这样我们的操作为 1;
5、下标为四的字符 s[4]='b'。对应的字符串为 "aaab",符合平衡字符串。同样将 b 计数,cnt=1;
6、下标为五的字符 s[5]='b'。对应的字符串为 "aaabb",符合平衡字符串。同样将 b 计数,cnt=2;
7、下标为六的字符 s[6]='a'。对应的字符串为 "aaabba",不符合平衡字符串。我们只需要删除最后的 a 就可以变成平衡字符串 “aaabb”,因此 cnt 减一变成 1;这样我们的操作为 2;
8、下标为七的字符 s[7]='b'。对应的字符串为 "aaabbb",符合平衡字符串。同样将 b 计数,cnt=2;
这样,我们得到平衡字符串 “aaabbb”,操作的次数为 2。
这里,我们观察到,当字符为 a 的时候,要考虑字符 b 的计数情况。如果计数为零,说明到目前位置,没有出现过字符 b,自然字符串是平衡的,不需要任何操作。如果计数不为零,说明出现了字符 b,字符串不是平衡的,我们可以删除一个字符,让字符串变成平衡,这样我们将 cnt 减一,操作数 ans 加一。
1、下标为零的字符 s[0]='b'。对应的字符串为 "b",符合平衡字符串,所以不需要处理,但是我们记录 cnt=1;
2、下标为一的字符 s[1]='b'。对应的字符串为 "bb",符合平衡字符串,所以不需要处理,但是我们记录 cnt=2;
3、下标为二的字符 s[2]='a'。对应的字符串为 "bba",不符合平衡字符串。要变成平衡字符串,只需要删除最后的字符即可,这样平衡字符串为 "bb"。这样我们 ans=1,cnt=1。
4、下标为三的字符 s[3]='a'。对应的字符串为 "bba",不符合平衡字符串。要变成平衡字符串,只需要删除最后的字符即可,这样平衡字符串为 "aa"(由于 cnt=0,b 和 a 就是等价的)。这样我们 ans=2,cnt=0。
5、下标为四的字符 s[4]='a'。对应的字符串为 "aaa",符合平衡字符串,所以不需要处理;
6、下标为五的字符 s[5]='a'。对应的字符串为 "aaaa",符合平衡字符串,所以不需要处理;
7、下标为六的字符 s[6]='a'。对应的字符串为 "aaaaa",符合平衡字符串,所以不需要处理;
8、下标为七的字符 s[7]='b'。对应的字符串为 "aaaaab",符合平衡字符串,所以不需要处理;
9、下标为八的字符 s[8]='b'。对应的字符串为 "aaaaabb",符合平衡字符串,所以不需要处理。
从上面的数据分析,我们可以看出,使用贪心是可以的。伪码为:
i 从 0 到结束遍历字符串的每一个字符
如果字符为 b,cnt加一。
如果字符为 a,如果 cnt 大于 0,说明不平衡,ans加一,cnt减一
class Solution {
public:
int minimumDeletions(string s) {
int cnt=0;//字母b
int ans=0;
for (int i=0; i0) {
ans++;
cnt--;
}
}
}
return ans;
}
};
O(N)
O(1)
本题通过删除字符达到字符串平衡,因此可以考虑使用动态规划的思路。我们知道动态规划主要是有以下几个部分组成:
1、动态规划数组定义;
2、初始值;
3、边界条件;
4、搜索方法
5、状态转移方程。
7、返回值
由于本题可以删除字符 a,也可以删除字符 b。因此我们考虑使用两个数组来表示。即 dpa 和 dpb。
dpa 表示字符串以 a 结尾且字符串是平衡最少删除次数。dpa[i] 表示到第 i 位结束最少操作次数。
dpb 表示字符串以 b 结尾且字符串是平衡最少删除次数。dpp[i] 表示到第 i 位结束最少操作次数。
开始的时候,字符串肯定是平衡的。因此 dpa[0]=0,dpb[0]=0。
没有。
自然是从 0 开始向 n 搜索。
根据 s[i] 的数据,我们可以得出状态转移方程:
如果 s[i]='a',我们将在原来已经平衡的字符串后面加上一个字符 a。对于 dpa 而言,加上 a 后,字符串还是平衡的,因此 dpa[i+1]=dpa[i];对于 dpb 而言,加上 a 后,字符串肯定是不平衡的,我们需要删除这个字符 a,才可以达到字符串是平衡而且以 b 位结尾,因此 dpb[i+1]=dpb[i]+1,就是多操作一次。
如果 s[i]='b',我们将在原来已经平衡的字符串后面加上一个字符 b。对于 dpa 而言,加上 b 后,字符串肯定是不平衡的,我们需要删除这个字符 b,才可以达到字符串是平衡而且以 a 位结尾,因此 dpa[i+1]=dpa[i]+1;对于 dpb 而言,加上 b 后,字符串还是平衡的,因此 dpb[i+1]=dpb[i]。
自然是 dpa[len] 和 dpb[len] 最小值。
class Solution {
public:
int minimumDeletions(string s) {
int len=s.length();
if (0==len) {
return 0;
}
vector dpa(len+1, 0);//以a结尾
vector dpb(len+1, 0);//以b结尾
for (int i=2; i
O(N)
O(N)