题目描述(中等难度)
算法1
(动态规划)
设S中前i个字符组成的子串为str,
表示使str单调递增且str的第i个字符是1的最小翻转次数,
表示使str单调递增且str的第i个字符是0的最小翻转次数
- 遍历S中的每个元素,根据元素是1还是0,分别更新f[i]和g[i]即可
思路跟LeetCode1186. 删除一次得到子数组最大和差不多
时间复杂度是 ,空间复杂度是 :额外申请了vector容器f和g
C++代码
class Solution {
public:
int minFlipsMonoIncr(string S) {
// 动态规划
// 设S中前i个字符组成的子串为str
// f[i]表示使str单调递增且str的第i个字符是1的最小翻转次数
// g[i]表示使str单调递增且str的第i个字符是0的最小翻转次数
int n = S.size();
vector<int> f(n + 1, 0), g(n + 1, 0);
for (int i = 1; i <= n; i ++) {
if (S[i - 1] == '1') {
f[i] = min(f[i - 1], g[i - 1]);
g[i] = 1 + g[i - 1];
} else {
f[i] = 1 + min(f[i - 1], g[i - 1]);
g[i] = g[i - 1];
}
}
return min(f[n], g[n]);
}
};
算法2
(模拟+预处理)
- 翻转后字符串一定是连续的一段0,然后连续的一段1(或全是0或全是1)
- 对于第i个字符,我们假设它后面的翻转成了1,它前面的(包含它)全部变成了0,那么操作次数是
第i个字符以前(包括它)1的个数
+第i个字符以后(不包括它)0的个数
- 遍历每个字符,维护最小操作次数即可
如:i = 3,看第3个字符
翻转前:101|001
翻转后:000|111
操作次数:2 + 1 = 3
- 注意:我们这样考虑时一定是包含至少一个0的,所以全是1的情况是特殊情况,要额外讨论
时间复杂度是 ,空间复杂度是
C++代码
class Solution {
public:
int minFlipsMonoIncr(string S) {
int n = S.size();
vector<int> dp(n + 1, 0);
// 预处理:dp[i] 表示第i个字符以前(包括它)有多少个1
for (int i = 1; i <= n; i ++) {
dp[i] = dp[i - 1] + (S[i - 1] == '1');
}
int res = n - dp[n]; // 特殊情况 '11011',翻转后全是1的情况
for (int i = 1; i <= n; i ++) {
int zeros = n - i - dp[n] + dp[i]; // 第i个字符以后(不包括它)有多少个0
res = min(res, zeros + dp[i]);
}
return res;
}
};
写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)