当前位置: 首页 > news >正文

算法题 将字符串翻转到单调递增

926. 将字符串翻转到单调递增

问题描述

如果一个二进制字符串的每个字符都满足:'0''1'之前(即形如"000...111..."),则称该字符串为单调递增的。

给定一个二进制字符串s,你可以将其中的任意'0'翻转为'1',或将'1'翻转为'0'

返回使s单调递增所需的最少翻转次数

示例

输入: s = "00110" 输出: 1 解释: 翻转最后一位得到 "00111"。 输入: s = "010110" 输出: 2 解释: 翻转第二位和第五位得到 "000111"。 输入: s = "00011000" 输出: 2 解释: 翻转第五位和第六位得到 "00000000"。

算法思路

  1. 前缀和:对于每个可能的分割位置i0 <= i <= n):

    • 左边[0, i-1]应该全是'0',需要翻转的'1'数量 = 左边'1'的数量
    • 右边[i, n-1]应该全是'1',需要翻转的'0'数量 = 右边'0'的数量
    • 总翻转次数 = 左边'1'的数量 + 右边'0'的数量
  2. 动态规划:维护两个状态:

    • dp0:以当前位置结尾,且当前字符为'0'时的最少翻转次数
    • dp1:以当前位置结尾,且当前字符为'1'时的最少翻转次数
    • 状态转移:
      • 如果当前字符是'0'dp0不变,dp1 = min(dp0, dp1) + 1
      • 如果当前字符是'1'dp0++dp1 = min(dp0, dp1)

代码实现

方法一:前缀和

classSolution{/** * 使用前缀和计算最少翻转次数 * * @param s 二进制字符串 * @return 使字符串单调递增的最少翻转次数 */publicintminFlipsMonoIncr(Strings){intn=s.length();// 计算前缀和:prefixOnes[i] 表示 s[0...i-1] 中 '1' 的数量int[]prefixOnes=newint[n+1];for(inti=0;i<n;i++){prefixOnes[i+1]=prefixOnes[i]+(s.charAt(i)=='1'?1:0);}intminFlips=Integer.MAX_VALUE;// 枚举所有可能的分割点 i(0 <= i <= n)// 分割点 i 表示 [0, i-1] 为 '0',[i, n-1] 为 '1'for(inti=0;i<=n;i++){// 左边 [0, i-1] 中 '1' 的数量(需要翻转为 '0')intonesToLeft=prefixOnes[i];// 右边 [i, n-1] 中 '0' 的数量(需要翻转为 '1')intzerosToRight=(n-i)-(prefixOnes[n]-prefixOnes[i]);minFlips=Math.min(minFlips,onesToLeft+zerosToRight);}returnminFlips;}}

方法二:动态规划

classSolution{/** * 动态规划 * * @param s 二进制字符串 * @return 使字符串单调递增的最少翻转次数 */publicintminFlipsMonoIncr(Strings){// dp0: 当前位置为 '0' 时的最少翻转次数// dp1: 当前位置为 '1' 时的最少翻转次数intdp0=0,dp1=0;for(charc:s.toCharArray()){if(c=='0'){// 当前字符是 '0'// dp0 不变(保持为 '0',不需要翻转)// dp1 = min(dp0, dp1) + 1(翻转为 '1')dp1=Math.min(dp0,dp1)+1;}else{// 当前字符是 '1'// dp0++(翻转为 '0')// dp1 = min(dp0, dp1)(保持为 '1',不需要翻转)dp0++;dp1=Math.min(dp0,dp1);}}returnMath.min(dp0,dp1);}}

算法分析

方法时间复杂度空间复杂度
前缀和O(n)O(n)
动态规划O(n)O(1)

算法过程

输入:s = "010110"

方法一

  1. prefixOnes = [0,0,1,1,2,3,3]
  2. 枚举分割点:
    • i=0: 左边’1’数=0,右边’0’数=3 → 翻转=3
    • i=1: 左边’1’数=0,右边’0’数=2 → 翻转=2
    • i=2: 左边’1’数=1,右边’0’数=2 → 翻转=3
    • i=3: 左边’1’数=1,右边’0’数=1 → 翻转=2
    • i=4: 左边’1’数=2,右边’0’数=1 → 翻转=3
    • i=5: 左边’1’数=3,右边’0’数=1 → 翻转=4
    • i=6: 左边’1’数=3,右边’0’数=0 → 翻转=3
  3. 最小值 = 2

方法二

  1. c='0':dp0=0(保持0),dp1=1(翻转为1)→(0,1)
  2. c='1':dp0=1(翻转为0),dp1=min(0,1)=0(保持1)→(1,0)
  3. c='0':dp0=1(保持0),dp1=min(1,0)+1=1(翻转为1)→(1,1)
  4. c='1':dp0=2(翻转为0),dp1=min(1,1)=1(保持1)→(2,1)
  5. c='1':dp0=3(翻转为0),dp1=min(2,1)=1(保持1)→(3,1)
  6. c='0':dp0=3(保持0),dp1=min(3,1)+1=2(翻转为1)→(3,2)
  7. 结果:min(3,2) = 2

测试用例

publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例1:标准示例System.out.println("Test 1: "+solution.minFlipsMonoIncr("00110"));// 1// 测试用例2:另一个标准示例System.out.println("Test 2: "+solution.minFlipsMonoIncr("010110"));// 2// 测试用例3:复杂情况System.out.println("Test 3: "+solution.minFlipsMonoIncr("00011000"));// 2// 测试用例4:全0System.out.println("Test 4: "+solution.minFlipsMonoIncr("0000"));// 0// 测试用例5:全1System.out.println("Test 5: "+solution.minFlipsMonoIncr("1111"));// 0// 测试用例6:交替System.out.println("Test 6: "+solution.minFlipsMonoIncr("010101"));// 3// 测试用例7:单字符System.out.println("Test 7: "+solution.minFlipsMonoIncr("0"));// 0System.out.println("Test 8: "+solution.minFlipsMonoIncr("1"));// 0// 测试用例8:需要全翻转为0System.out.println("Test 9: "+solution.minFlipsMonoIncr("1111100000"));// 5// 测试用例9:需要全翻转为1System.out.println("Test 10: "+solution.minFlipsMonoIncr("0000011111"));// 0// 测试用例10:长字符串StringlongStr="00000000000000000000000000000000000000000000000000";System.out.println("Test 11: "+solution.minFlipsMonoIncr(longStr));// 0}

关键点

  1. 分割点

    • 单调递增字符串必然存在一个分割点
    • 枚举所有可能的分割点是最直观的思路
  2. 动态规划状态

    • dp0dp1分别表示以'0''1'结尾的最少翻转次数
    • 状态转移要考虑当前字符和之前的最优解
  3. 边界情况处理

    • '0'或全'1'的情况
    • 单字符字符串
    • 空字符串

常见问题

  1. 为什么动态规划中dp1 = min(dp0, dp1)
    • 单调递增允许'1'后面继续是'1'
    • 前面可以是以'0''1'结尾,取较小值
http://www.jsqmd.com/news/239424/

相关文章:

  • 新手必看的HBuilderX安装教程:超详细版配置指南
  • 通义千问2.5-0.5B优化技巧:让边缘设备推理速度提升3倍
  • Nodejs和vue框架的基于智能推荐的卫生健康系统的设计与实现
  • 5分钟部署Qwen2.5-0.5B:零基础搭建法律问答机器人实战
  • HunyuanVideo-Foley创新应用:游戏过场动画音效自动生成探索
  • Nodejs和vue框架的基于的书城阅读器系统的设计与实现
  • 吐血推荐自考必用TOP10 AI论文平台测评
  • UDS服务在车载网络架构中的部署完整指南
  • 从零实现:基于SPICE的二极管钳位电路动态行为仿真
  • 动态打码技术演进:从传统方法到AI解决方案
  • 从零实现Keil5下载到PLC仿真系统的完整示例
  • 基于AI手势识别的远程控制方案:生产环境部署实战
  • 【Conda】Conda更换国内镜像源
  • GLM-4.6V-Flash-WEB实战对比:网页与API推理性能全面评测
  • 维纶触摸屏程序实际项目,威纶通界面UI,复制可用,威伦通触摸EB Pro6.00以上版本均可用...
  • MediaPipe Hands实战:AR应用中的手势交互实现
  • pgsql_tmp文件夹体积快速增加
  • VibeVoice-TTS镜像免配置部署:JupyterLab一键启动实操手册
  • JVET-AI0084
  • 小白也能玩转机器翻译:手把手教你用HY-MT1.5-1.8B
  • 从零构建Claude Agent:Skills、Projects与MCP的架构设计与实践(建议收藏)
  • 考虑过网费用分摊的多产消者点对点能源交易分布式优化系统说明
  • MediaPipe Pose实战:舞蹈动作识别系统部署
  • 小白也能玩转大模型:手把手教你用HY-MT1.5-1.8B搭建离线翻译服务
  • MediaPipe模型部署:AI人脸隐私卫士环境配置
  • 基于CAN总线的UDS NRC错误响应处理详解
  • MediaPipe姿态识别误检规避:背景复杂场景优化策略
  • RTX3060跑出180token/s:通义千问2.5-0.5B性能测试
  • es连接工具数据传输安全机制:图解说明
  • 灵活用工系统:打破传统边界的未来企业引擎