这是一道我觉得很不错的题目,值得学习的深究
考点:字符串,动态规划,枚举法
题目:
给你一个字符串 s ,它仅包含字符 'a' 和 'b' 。
你可以删除 s 中任意数目的字符,使得 s 平衡 。当不存在下标对 (i,j) 满足 i < j ,且 s[i] = 'b' 的同时 s[j]= 'a' ,此时认为 s 是 平衡 的。
请你返回使 s 平衡 的 最少 删除次数。
示例 1:
输入:s = "aababbab"
输出:2
解释:你可以选择以下任意一种方案:
下标从 0 开始,删除第 2 和第 6 个字符("aababbab" -> "aaabbb"),
下标从 0 开始,删除第 3 和第 6 个字符("aababbab" -> "aabbbb")。
示例 2:
输入:s = "bbaaaaabb"
输出:2
解释:唯一的最优解是删除最前面两个字符。
题目解读:
字符串想要最终得到一串前面全是a,后面全是b
法一:
枚举法
遍历数组,先找a的个数,这会是答案的一种可能,因为大不了就把a全部删掉
从i=0开始遍历数组,我们假设从小于等于i的数全是a,i一直往后,一共就是这些可能的情况,所有的情况中最小值就是最优解
因为第一步已经求出来a的总数,所以随着i的变化不同情况对a的“看法”也是不一样的,也就是归属于左边的话那就是合理的,最后的总count可以--,同时,i的左边也会包含很多b,这些b也要删掉
class Solution {
public:
int minimumDeletions(string s) {
int counta=0;
int countb=0;
int count=0;
for(auto l:s)
{
if(l=='a')//统计a的总数
counta++;
}
count=counta;//count的一种情况
for(int i=0;i<s.size();i++)
{
//开始对i所到之处逐个排查
if(s[i]=='b')
countb++;
if(s[i]=='a')
counta--;
count=min(count,(counta+countb));
}
return count;
}
};
法二:
动态规划法
动归的思想:
状态定义 f[i]看作第i个元素之前的字符串需要调整好的至少调整次数
如果第i个字符是b那么此次f[i]和f[i-1]是一样的,因为前面已经调整好了,i的位置是b完全符合条件
如果第i个字符是a则需要分类讨论
1.把i前面的b全部消失的调整次数就是一种f[i]
2.如果i位置的a消失也就没有需要调整的烦恼了,a就只能牺牲掉自己,那么f[i]=f[i-1]+1(1就是把a删掉)
由此列出如果s[i]=='a'的状态方程 f[i]=(min(f[i-1]+1),countb)
因此如果遍历到b并不是什么都不需要做,还要记录下b的总数
class Solution {
public:
int minimumDeletions(string s) {
int countb=0;
int f=0;
for(auto& l:s)
{
if(l=='b')
countb++;
else
f=min(f+1,countb);
}//每次遍历都是求出当前字符以及之前的所有字符让其满足条件的最少调整次数,当遍历完,就可以得到最后一个字符之前的也就是整个字符串所需要的最少调整次数
return f;
}
};