小M的最小操作代价函数

问题描述

小M给你一个长度为nn的01串,串只包含字符 '0' 和 '1'。你可以从这个串中删除一个前缀和一个后缀(可以为空),即保留原串的一个连续子串。操作后的代价包括两部分:

  1. 被删除的字符 '1' 的数量;
  2. 剩余子串中字符 '0' 的数量。

你的任务是找出如何进行操作,才能使代价最小,并输出这个最小代价。


测试样例

样例1:

输入:s = "101110110"
输出:2

样例2:

输入:s = "111011"
输出:1

样例3:

输入:s = "0000"
输出:0

Python实现

思路说明

题目要求通过删除一个前缀和一个后缀,使得操作后的代价最小。代价包括两部分:被删除的字符 '1' 的数量和剩余子串中字符 '0' 的数量。我们可以通过前缀和数组来快速计算任意子串中 '0' 和 '1' 的数量,然后通过双层循环遍历所有可能的子串,计算每种情况下的代价,并取最小值。

解题过程

  1. 前缀和数组的构建

    • 使用两个前缀和数组 sum1 和 sum2,分别记录从字符串开始到当前位置的 '0' 和 '1' 的数量。
    • 遍历字符串 s,根据字符是 '0' 还是 '1',更新 sum1 和 sum2
  2. 初始化答案

    • 初始化答案 ans 为字符串中所有 '1' 的数量,即 sum2[n],表示不删除任何前缀和后缀的情况。
  3. 双层循环遍历所有子串

    • 外层循环遍历子串的起点 i,内层循环遍历子串的终点 j
    • 对于每个子串 s[i-1:j],计算其代价:
      • 剩余子串中 '0' 的数量为 sum1[j] - sum1[i-1]
      • 被删除的前缀中 '1' 的数量为 sum2[i-1]
      • 被删除的后缀中 '1' 的数量为 sum2[n] - sum2[j]
    • 更新答案 ans 为所有情况中的最小值。
  4. 返回结果

    • 最终返回 ans,即最小的代价。

复杂度分析

  • 时间复杂度

    • 构建前缀和数组的时间复杂度为 O(n)。
    • 双层循环遍历所有子串的时间复杂度为 O(n2)。
    • 因此,总的时间复杂度为 O(n2)。
  • 空间复杂度

    • 使用两个前缀和数组 sum1 和 sum2,空间复杂度为 O(n)。

知识点扩展

  • 前缀和数组

    • 前缀和数组是一种常用的技巧,用于快速计算任意子数组或子串的和或数量。在本题中,我们使用前缀和数组来记录 '0' 和 '1' 的数量,从而在 O(1) 时间内计算任意子串中 '0' 和 '1' 的数量。
  • 动态规划

    • 虽然本题没有显式地使用动态规划,但通过遍历所有可能的子串并计算代价,本质上是一种动态规划的思想。通过前缀和数组和双层循环,我们能够有效地解决问题。
  • 双层循环优化

    • 在实际应用中,如果 n 较大,O(n2) 的时间复杂度可能会导致性能问题。可以通过进一步优化算法,例如使用滑动窗口或单调队列等技巧,将时间复杂度降低到 O(n) 或 O(nlogn)。

代码实现

def solution(s):
    n = len(s)
    sum1 = [0] * (n + 1)  # 前缀中 '0' 的数量
    sum2 = [0] * (n + 1)  # 前缀中 '1' 的数量

    # 计算前缀中 '0' 和 '1' 的数量
    for i in range(1, n + 1):
        sum1[i] = sum1[i - 1]
        sum2[i] = sum2[i - 1]
        if s[i - 1] == '0':
            sum1[i] += 1
        else:
            sum2[i] += 1

    ans = sum2[n]  # 初始化答案为所有 '1' 的数量

    # 遍历所有可能的子串
    for i in range(1, n + 1):
        for j in range(i, n + 1):
            ans = min(ans, sum1[j] - sum1[i - 1] + sum2[i - 1] + sum2[n] - sum2[j])

    return ans

# 测试用例
print(solution("101110110") == 2)
print(solution("111011") == 1)
print(solution("0000") == 0)

C++实现

问题理解

你有一个只包含字符 '0' 和 '1' 的字符串 s。你可以删除一个前缀和一个后缀,保留一个连续的子串。操作的代价包括两部分:

  1. 被删除的字符 '1' 的数量。
  2. 剩余子串中字符 '0' 的数量。

目标是找到一种删除方式,使得总代价最小。

代码实现

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int solution(string s) {
    int n = s.length();
    vector<int> sum1(n + 1, 0);  // 前缀中 '0' 的数量
    vector<int> sum2(n + 1, 0);  // 前缀中 '1' 的数量

    // 计算前缀中 '0' 和 '1' 的数量
    for (int i = 1; i <= n; ++i) {
        sum1[i] = sum1[i - 1];
        sum2[i] = sum2[i - 1];
        if (s[i - 1] == '0') {
            sum1[i] += 1;
        } else {
            sum2[i] += 1;
        }
    }

    int ans = sum2[n];  // 初始化答案为所有 '1' 的数量

    // 遍历所有可能的子串
    for (int i = 1; i <= n; ++i) {
        for (int j = i; j <= n; ++j) {
            ans = min(ans, sum1[j] - sum1[i - 1] + sum2[i - 1] + sum2[n] - sum2[j]);
        }
    }

    return ans;
}

int main() {
    // 测试用例
    cout << (solution("101110110") == 2) << endl;
    cout << (solution("111011") == 1) << endl;
    cout << (solution("0000") == 0) << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值