【备战秋招】每日一题:4月23日美团春招第四题:题面+题目思路 + C++/python/js/Go/java带注释

 为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第四题-01串的代价

在线评测链接:P1248

题目内容

塔子哥是一个喜欢研究密码的人,他经常在网上寻找各种有趣的密码挑战。他最近发现了一个神秘的网站,网站上只有一个输入框和一个提交按钮,没有任何提示。塔子哥好奇地输入了一些内容,发现网站会返回一个长度为 n 的 01 串,只包含字符 01 的串。塔子哥觉得这个串一定是一个密码的线索,他想要破解它。

塔子哥仔细观察了这个串,发现它里面隐藏着一些规律,比如有些位置的字符总是相同的,有些位置的字符总是相反的。塔子哥猜测这些规律可能是密码的组成部分,他想要把它们都保留下来,并且删除掉无关的字符。但是,塔子哥不想删除太多的字符,也不想保留太长的串。所以,他想知道,在保证删除一个前缀和一个后缀的情况下(前缀和后缀都可以为空),即保留一个原串的连续子串(可以为空),他需要最小化以下代价:

  1. 被删除的字符 1 的个数;

  2. 剩下的子串的字符 0 的个数。

塔子哥会给你总共若干次询问,每次询问他会告诉你网站返回给他的 01 串。你需要帮助塔子哥判断,在每次询问中,他需要输出的最小代价是多少。

输入描述

一个长度为n(1 \leq n \leq 1e5)的 01 字符串。

输出描述

一个整数,表示最小的操作代价。

样例1

输入

101110110

输出

2

样例解释

删除前两个字符和最后一个字符,代价为 1 ;

保留下来的字符中有一个 0 ,代价为 1 ,故代价之和为 2 。

样例2

输入

1010110001

输出

3

思路

思维 + 贪心

由于可以删除一个前缀和一个后缀,故可以这么考虑: 枚举分界点 i,[1,i] 中删除一个前缀,[i+1,n] 中删除一个后缀。取两部分代价之和的最小值。

具体删除的步骤:

  • 如果遇到一个 1 ,则不删除,记录下来。

  • 如果遇到一个 0 ,对于删除到这个 0 的代价,和保留这个 0 的代价,两者取 min ,对于保留这个 0 的代价,就是 +1 ,对于删除到这个 0 的代价,就是将目前所有存储的 1 都删除。

时间复杂度:O(n)

类似题目推荐

LeetCode

LeetCode上的贪心题,代码随想录总结的非常好了,见 贪心 - 代码随想录

Codefun2000

  1. P1091. 米哈游 2023.03.19-第一题-交换字符

  2. P1235. 百度 2023.04.15-实习-第一题-字符串前缀

  3. P1005. 腾讯 2022.10.16-汽车

  4. P1137 美团 2023.04.01-第一题-整理

  5. P1077 美团 2023.3.11-第一题-字符串修改

  6. P1024 百度 2022.9.13-01反转

  7. P1089 美团 2023.3.18.10点-第三题-塔子哥的回文串

代码

CPP

#include <bits/stdc++.h>
using namespace std;
​
int main()
{
    string s;
    cin >> s;
​
    int n = s.size();
​
    vector<int> suf(n + 1);
    suf[n] = 0;
​
    int one = 0, val = 0;
    // 预处理出 suf[i] ,suf[i] 表示 [i, n - 1] 这个串删除后缀后的最小代价
    for (int i = n - 1; i >= 0; --i) {
        // 如果是 0
        if (s[i] == '0') {
            if (one > 0) {
                // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
                // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
                one -= 1;
                val += 1;
            } 
​
            // else {
            //     如果不存在 1,这个 0 可以直接删
            // } 
        } else {
            // 如果遇到的是 1,则先不删除,保留下来,和之后遇到的 0 相抵
            one += 1;
        }
        suf[i] = val;
    }
​
    one = val = 0;
    int ans = suf[0];
    // 枚举 i 为分界点
    // 1. 在 [0, i] 这个串删除前缀的最小值,动态维护
    // 2. 在 [i+1, n-1] 这个串删除后缀的最小值suf[i+1],已经预处理
    for (int i = 0; i < n; ++i) {
        if (s[i] == '0') {
            // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
            // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
            if (one > 0) {
                one -= 1;
                val += 1;
            }
        } else {
            one += 1;
        }
        ans = min(ans, suf[i + 1] + val);
    }
​
    cout << ans << "\n";
​
    return 0;
}

python

s = input()
​
n = len(s)
​
suf = [0] * (n + 1)
​
one, val = 0, 0
# 预处理出 suf[i] ,suf[i] 表示 [i, n - 1] 这个串删除后缀后的最小代价
for i in range(n - 1, -1, -1):
    # 如果是 0
    if s[i] == '0':
        if one > 0:
            # 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
            # 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
            one -= 1
            val += 1
        # else:
        #     如果不存在 1,这个 0 可以直接删
    else:
        # 如果遇到的是 1,则先不删除,保留下来,和之后遇到的 0 相抵
        one += 1
    suf[i] = val
​
one, val = 0, 0
ans = suf[0]
# 枚举 i 为分界点
# 1. 在 [0, i] 这个串删除前缀的最小值,动态维护
# 2. 在 [i+1, n-1] 这个串删除后缀的最小值suf[i+1],已经预处理
for i in range(n):
    if s[i] == '0':
        # 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
        # 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
        if one > 0:
            one -= 1
            val += 1
    else:
        one += 1
    ans = min(ans, suf[i + 1] + val)
​
print(ans)

Java

import java.util.Scanner;
​
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.next();
        int n = s.length();
​
        int[] suf = new int[n + 1];
        suf[n] = 0;
        int one = 0, val = 0;
​
        // 预处理出 suf[i] ,suf[i] 表示 [i, n - 1] 这个串删除后缀后的最小代价
        for (int i = n - 1; i >= 0; --i) {
            // 如果是 0
            if (s.charAt(i) == '0') {
                if (one > 0) {
                    // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
                    // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
                    one -= 1;
                    val += 1;
                }
                // else {
                //     如果不存在 1,这个 0 可以直接删
                // } 
            } else {
                // 如果遇到的是 1,则先不删除,保留下来,和之后遇到的 0 相抵
                one += 1;
            }
            suf[i] = val;
        }
​
        one = val = 0;
        int ans = suf[0];
​
        // 枚举 i 为分界点
        // 1. 在 [0, i] 这个串删除前缀的最小值,动态维护
        // 2. 在 [i+1, n-1] 这个串删除后缀的最小值suf[i+1],已经预处理
        for (int i = 0; i < n; ++i) {
            if (s.charAt(i) == '0') {
                // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
                // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
                if (one > 0) {
                    one -= 1;
                    val += 1;
                }
            } else {
                one += 1;
            }
            ans = Math.min(ans, suf[i + 1] + val);
        }
​
        System.out.println(ans);
    }
}

Go

package main
​
import "fmt"
​
func main() {
    var s string
    fmt.Scanln(&s)
​
    n := len(s)
    suf := make([]int, n+1)
​
    suf[n] = 0
    one := 0
    val := 0
    // 预处理出 suf[i] ,suf[i] 表示 [i, n - 1] 这个串删除后缀后的最小代价
    for i := n - 1; i >= 0; i-- {
        if s[i] == '0' {
            // 如果是 0
            if one > 0 {
                // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
                // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
                one -= 1
                val += 1
            }
​
            // else {
            //     如果不存在 1,这个 0 可以直接删
            // } 
        } else {
            // 如果遇到的是 1,则先不删除,保留下来,和之后遇到的 0 相抵
            one += 1
        }
        suf[i] = val
    }
​
    one = 0
    val = 0
    ans := suf[0]
​
    // 枚举 i 为分界点
    // 1. 在 [0, i] 这个串删除前缀的最小值,动态维护
    // 2. 在 [i+1, n-1] 这个串删除后缀的最小值suf[i+1],已经预处理
    for i := 0; i < n; i++ {
        if s[i] == '0' {
            // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
            // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
            if one > 0 {
                one -= 1
                val += 1
            }
        } else {
            one += 1
        }
​
        ans = min(ans, val+suf[i+1])
    }
​
    fmt.Println(ans)
}
​
func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}
​

Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
    return;
});
process.stdin.on('end', () => {
    const s = input.trim();
​
    const n = s.length;
​
    const suf = new Array(n + 1);
    suf[n] = 0;
​
    let one = 0, val = 0;
    // 预处理出 suf[i] ,suf[i] 表示 [i, n - 1] 这个串删除后缀后的最小代价
    for (let i = n - 1; i >= 0; --i) {
        // 如果是 0
        if (s[i] === '0') {
            if (one > 0) {
                // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
                // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
                one -= 1;
                val += 1;
            } 
​
            // else {
            //     如果不存在 1,这个 0 可以直接删
            // } 
        } else {
            // 如果遇到的是 1,则先不删除,保留下来,和之后遇到的 0 相抵
            one += 1;
        }
        suf[i] = val;
    }
​
    one = val = 0;
    let ans = suf[0];
    // 枚举 i 为分界点
    // 1. 在 [0, i] 这个串删除前缀的最小值,动态维护
    // 2. 在 [i+1, n-1] 这个串删除后缀的最小值suf[i+1],已经预处理
    for (let i = 0; i < n; ++i) {
        if (s[i] === '0') {
            // 如果存在 1,就要保留这个 0 了,但是要维护一个 one 的数量,
            // 如果 one 的数量为 0 后,下次遇到 0 就不如将他们都删了。
            if (one > 0) {
                one -= 1;
                val += 1;
            }
        } else {
            one += 1;
        }
        ans = Math.min(ans, suf[i + 1] + val);
    }
​
    console.log(ans);
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值