LeetCode 题解 | 926. 将字符串翻转到单调递增(动态规划 C++)

题目描述(中等难度)

原题链接
在这里插入图片描述

算法1

(动态规划) O ( n ) O(n)
设S中前i个字符组成的子串为str, f [ i ] f[i] 表示使str单调递增且str的第i个字符是1的最小翻转次数, g [ i ] g[i] 表示使str单调递增且str的第i个字符是0的最小翻转次数

  • 遍历S中的每个元素,根据元素是1还是0,分别更新f[i]和g[i]即可

思路跟LeetCode1186. 删除一次得到子数组最大和差不多

时间复杂度是 O ( n ) O(n) ,空间复杂度是 O ( n ) O(n) :额外申请了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

(模拟+预处理) O ( n ) O(n)

  • 翻转后字符串一定是连续的一段0,然后连续的一段1(或全是0或全是1)
  • 对于第i个字符,我们假设它后面的翻转成了1,它前面的(包含它)全部变成了0,那么操作次数是第i个字符以前(包括它)1的个数 + 第i个字符以后(不包括它)0的个数
  • 遍历每个字符,维护最小操作次数即可
如:i = 3,看第3个字符
    翻转前:101|001
    翻转后:000|111
   操作次数:2 + 1 = 3
  • 注意:我们这样考虑时一定是包含至少一个0的,所以全是1的情况是特殊情况,要额外讨论

时间复杂度是 O ( n ) O(n) ,空间复杂度是 O ( n ) O(n)

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;
    }
};

写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)

发布了239 篇原创文章 · 获赞 80 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/104295722