2938. 区分黑球与白球

2024-06-07 00:20
文章标签 区分 2938 黑球 白球

本文主要是介绍2938. 区分黑球与白球,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目

桌子上有 n 个球,每个球的颜色不是黑色,就是白色。

给你一个长度为 n 、下标从 0 开始的二进制字符串 s,其中 10 分别代表黑色和白色的球。

在每一步中,你可以选择两个相邻的球并交换它们。

返回「将所有黑色球都移到右侧,所有白色球都移到左侧所需的 最小步数」。

示例 1:

输入s = "101"

输出1

解释:我们可以按以下方式将所有黑色球移到右侧:

  • 交换 s[0]s[1]s = "011"
    最开始,1 没有都在右侧,需要至少 1 步将其移到右侧。

示例 2:

输入s = "100"

输出2

解释:我们可以按以下方式将所有黑色球移到右侧:

  • 交换 s[0]s[1]s = "010"
  • 交换 s[1]s[2]s = "001"
    可以证明所需的最小步数为 2

示例 3:

输入s = "0111"

输出0

解释:所有黑色球都已经在右侧。

提示:

  • 1 <= n == s.length <= 10^5
  • s[i] 不是 '0',就是 '1'

代码

完整代码

#include <string.h>
#include <stdio.h>
long long minimumSteps(char* s) {int n = strlen(s);long long res = 0;long long cntof1 = 0;for (int i = 0; i < n; i++){if(s[i] == '1'){cntof1 ++;}else{res += cntof1;}}return res;
}

思路分析

该代码的目的是计算将所有黑色球(‘1’)移到右侧,所有白色球(‘0’)移到左侧所需的最小步数。算法核心类型是贪心算法,通过遍历字符串 s,每当遇到一个白色球(‘0’),计算将其左侧的所有黑色球(‘1’)移到其右侧所需的步数,并累加这些步数,从而得到最小步数。

拆解分析

  1. 变量初始化

    int n = strlen(s);
    long long res = 0;
    long long cntof1 = 0;
    
    • n:存储字符串 s 的长度。
    • res:存储最终结果,即最小步数。
    • cntof1:用于统计遍历过程中遇到的黑色球(‘1’)的数量。
  2. 遍历字符串

    for (int i = 0; i < n; i++)
    {if (s[i] == '1'){cntof1++;}else{res += cntof1;}
    }
    
    • 遍历字符串 s 的每个字符:
      • 如果字符是 ‘1’(黑色球),cntof1 递增。
      • 如果字符是 ‘0’(白色球),res 增加当前 cntof1 的值。这意味着将当前白色球左侧的所有黑色球向右移动到该白色球的右侧所需的步数。
  3. 返回结果

    return res;
    
    • 返回总步数 res

复杂度分析

  • 时间复杂度

    • 遍历字符串 s 需要 O(n) 的时间,其中 n 是字符串的长度。
    • 在遍历过程中,只有简单的加法和判断操作,因此整体时间复杂度为 O(n)
  • 空间复杂度

    • 使用了几个额外的变量(nrescntof1),空间复杂度为 O(1)

综上所述,代码的时间复杂度为 O(n),空间复杂度为 O(1)

结果

结果

一题多解

分析最优性

当前算法的核心思路是贪心策略:每遇到一个白色球,统计其左侧黑色球的数量,并将这些黑色球移动到右侧。这种方法直接反映了问题的本质:我们只需要计算每个白色球左侧所有黑色球的位置差异,并累加这些差异即可。

更详细的贪心思路分析
  1. 遍历字符一次
    • 每个字符只需要访问一次,所以时间复杂度是 O(n)
  2. 累加步数
    • 计算步数时,只需一个累加操作,空间复杂度是 O(1)

为什么是最优的

  1. 时间复杂度

    • 对于一个长度为 n 的字符串,只需要遍历一遍,所以时间复杂度是 O(n)。没有办法在更短的时间内解决这个问题,因为至少需要访问每个字符一次。
  2. 空间复杂度

    • 只使用了常量级别的额外空间,即用于计数的几个变量。因此,空间复杂度是 O(1),没有多余的存储需求。

其他可能的思路

虽然当前方法已经最优,但为了完整性,这里列出其他一些常见的算法思路,并分析其复杂度:

  1. 双指针
    • 使用两个指针,一个从左到右找到第一个黑色球的位置,另一个从右到左找到第一个白色球的位置,然后交换它们。
    • 然而,这个方法在最坏情况下仍需要 O(n) 的时间来遍历整个字符串,而且每次交换操作的次数和复杂度会增加。
#include <stdio.h>
#include <string.h>long long minimumStepsTwoPointers(char* s) {int n = strlen(s);int left = 0, right = n - 1;long long res = 0;while (left < right) {while (left < n && s[left] == '0') left++;while (right >= 0 && s[right] == '1') right--;if (left < right) {res += (right - left);left++;right--;}}return res;
}

双指针

  1. 动态规划
    • 可以尝试构建一个动态规划表来记录状态转移,但这会引入额外的空间开销,而且复杂度分析会变得更复杂,可能不如贪心策略直接有效。
// 动态规划不太会
  1. 前缀和后缀数组
    • 构建前缀和后缀数组来记录每个位置的黑色球数量,这样可以在常数时间内计算出每个位置的移动步数。但构建这些数组需要额外的 O(n) 空间,不如贪心策略简洁。
#include <stdio.h>
#include <string.h>long long minimumStepsPrefixSuffix(char* s) {int n = strlen(s);int prefix[n + 1];int suffix[n + 1];memset(prefix, 0, sizeof(prefix));memset(suffix, 0, sizeof(suffix));for (int i = 1; i <= n; i++) {prefix[i] = prefix[i - 1] + (s[i - 1] == '1');}for (int i = n - 1; i >= 0; i--) {suffix[i] = suffix[i + 1] + (s[i] == '0');}long long res = 0;for (int i = 0; i < n; i++) {if (s[i] == '0') {res += prefix[i];}}return res;
}

前缀和

结论

综上所述,当前的贪心策略已经是最优解,无论是时间复杂度还是空间复杂度都是最优的,无法进一步优化。

这篇关于2938. 区分黑球与白球的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1037653

相关文章

MySQL表名区分大小写设置

打开 mysql配置文件mysqld.cnf 打开文件,找到[mysqld]在下面增加一行 lower_case_table_names=0 (0:大小写敏感;1:大小写不敏感) 重启mysql服务 docker restart mysqlserver

Circuit Design 贴片晶振的区分

贴片晶振脚位的区分(非常详细,尤其是如何区分四脚的有源无源晶振): http://ruitairt.com/Article/tiepian_1.html 如何区分有源和无源晶振: http://ruitairt.com/Article/yzjddbfqsq_1.html

Redis 命令不区分大小写,键值区分大小写Redis

今天才知道   Redis 命令不区分大小写   但键值区分大小写的

计算两个字符串的最大公共字符串的长度,字符不区分大小写

/*** */package testString;import java.util.Scanner;/***@author: Administrator*@date: 2016-12-28 下午01:08:30*/public class Main {public static void main(String[] args){Scanner sc=new Scanner(Syste

区分变压器损耗

磁芯损耗 铁芯损耗分为两类:涡流损耗和磁滞损耗。 磁滞损耗 当没有次级电流流动时,流过变压器初级绕组的电流会产生磁通量,从而在次级绕组中感应出电压。该初级电流称为励磁电流,由于初级绕组的 CEMF 较大,因此相当小。由于变压器是通过磁通量传输能量的设备,因此集中磁通量可提高变压器的效率。 初级绕组的磁通量缠绕在称为磁芯的铁或钢材料上,以集中磁通量。磁芯材料为磁通量提供了比露天更好的路径。磁

git 提交文件不区分大小写

关于git不区分文件名大小写的处理 2014-10-29 18:27 by Rollen Holt, 743 阅读, 0 评论, 收藏, 编辑 今天遇到了git不区分文件名大小写的问题,一开始着实郁闷了一把。 处理办法: windows下在git中修改文件的大小写 git mv --force myfile MyFile//如命令 : git mv --force src/mai

spring boot 项目 prometheus 自定义指标收集区分应用环境集群实例ip,使用 grafana 查询--方法耗时分位数指标

spring boot 项目 prometheus 自定义指标收集 auth @author JellyfishMIX - github / blog.jellyfishmix.comLICENSE LICENSE-2.0 说明 网上有很多 promehteus 和 grafana 配置,本文不再重复,只介绍自定义部分。目前只介绍了分位数指标的收集和查询,常用于方法耗时的指标监控。 自定

JQuery中,html()、text()、val()区分

1.html()用法 html是用来获取任意一个元素的内容,该元素必须是双标签元素(div、span、a等)。 <p>this is a text.</p> $('p').html();//得到的结果是p里面的所有内容,但是它能识别内部的html标签 2.text()用法 用法与html()一样,不过区别在于text()输出的结果是将标签内部的所有内

用Spring区分开发环境、测试环境、生产环境

我们在项目开发过程中,经常需要往开发环境、测试环境、生产环境部署程序。随着程序越来越复杂,配置文件的增多,如果每次部署都去改一遍配置文件,这种重复的工作会把程序员逼疯。     好在spring提供了这一自动切换的功能,简要说明如下:     1. 首先在applicationContext中,对需要配置的参数进行配置,以下图为例: <bean id="minaService" cl

详细区分回车和换行的关系

今天,我总算搞清楚“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别了。在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。于是,研制人员想了个办法解