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

2023大厂笔试模拟练习网站(含题解)
www.codefun2000.com
最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据,挂载到我们的OJ上,供大家学习交流,体会笔试难度。现已录入200+道互联网大厂模拟练习题,还在极速更新中。欢迎关注公众号“塔子哥学算法”获取最新消息。

提交链接:

https://codefun2000.com/p/P1138

为了更好的阅读体检,可以查看OJ上的题解。进入提交链接,点击右边菜单栏的"查看塔子哥的题解"

在线评测链接: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 
using namespace std;
​
int main()
{
    string s;
    cin >> s;
​
    int n = s.size();
​
    vector 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);
});

你可能感兴趣的:(备战2023秋招,java,c++,javascript,开发语言,python)