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

LeetCode算法日记 - Day 34: 二进制求和、字符串相乘

目录

1. 二进制求和

1.1 题目解析

1.2 解法

1.3 代码实现

2. 字符串相乘

2.1 题目解析

2.2 解法

2.3 代码实现


1. 二进制求和

67. 二进制求和 - 力扣(LeetCode)

给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。

示例 1:

输入:a = "11", b = "1"
输出:"100"

示例 2:

输入:a = "1010", b = "1011"
输出:"10101"

提示:

  • 1 <= a.length, b.length <= 104
  • a 和 b 仅由字符 '0' 或 '1' 组成
  • 字符串如果不是 "0" ,就不含前导零

1.1 题目解析

题目本质:
把两个仅含 0/1 的长字符串当成二进制大整数,相加并以二进制字符串返回。本质就是“逐位相加 + 维护进位”。

常规解法:
把二进制转成整型/长整型相加,再转回二进制。

问题分析:
长度可达 10^4,超出整型范围;而且频繁进制转换不必要、易溢出。应当直接在字符串层面做“竖式加法”。预计最佳复杂度:一次线性扫描 O(n)

思路转折:
要想高效且安全 → 不做数值转换 → 双指针从右到左逐位相加,sum = carry + bitA + bitB,当前位写 sum % 2,新的进位 sum / 2
这样无需补零,只要在循环里判断指针是否越界即可;最后把构建的结果反转即可得到正确顺序。

1.2 解法

算法思想:设 i = a.length()-1、j = b.length()-1、carry = 0。循环条件:i>=0 || j>=0 || carry!=0。 每轮:

  • sum = carry + (i>=0 ? a[i]-'0' : 0) + (j>=0 ? b[j]-'0' : 0)

  • 结果位:sum % 2

  • 新进位:sum / 2

i)置双指针到两串末尾,carry=0,准备 StringBuilder(可预分配容量 maxLen+1)。

ii)循环:只要任一串未遍历完或还有进位,就继续:

  • 读各自当前位(越界当 0);

  • 相加得 sum;

  • append(sum % 2) 到 sb(此时是低位在前);

  • carry = sum / 2;指针左移。

iii)循环结束后 sb.reverse() 得到最终结果。

易错点:

  • while 条件必须是 carry != 0,不能写成 carry >= 0,否则可能死循环。

  • charAt(i) 是字符,要先减 '0' 转为数值位。

  • 右到左加;别从左往右。

  • 注意不等长字符串:用 if (i>=0)/if (j>=0) 判断是否还能取位。

  • StringBuilder.append(int) 对 0/1是安全的;也可写 append((char)('0' + (sum & 1)))。

  • 别在循环里频繁字符串拼接 +,会产生大量中间对象,影响性能。

1.3 代码实现

class Solution {public String addBinary(String a, String b) {int i = a.length() - 1, j = b.length() - 1;int carry = 0;// 预估容量:最大长度 + 1(可能多出一个进位)StringBuilder sb = new StringBuilder(Math.max(a.length(), b.length()) + 1);while (i >= 0 || j >= 0 || carry != 0) {int sum = carry;if (i >= 0) sum += a.charAt(i--) - '0';if (j >= 0) sum += b.charAt(j--) - '0';sb.append(sum % 2);   // 当前位carry = sum / 2;      // 新进位}return sb.reverse().toString();}
}

复杂度分析:

  • 时间:O(n),其中 n = max(|a|, |b|),每位最多访问一次。

  • 空间:O(n),StringBuilder 存放结果(最坏多一位进位)。

2. 字符串相乘

43. 字符串相乘 - 力扣(LeetCode)

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。

2.1 题目解析

题目本质:
把两个十进制数字串当作“大整数”做乘法,返回其十进制字符串。可抽象为“竖式乘法在字符串层面的模拟”。

常规解法:
直接把 num1/num2 转为内置整数或 BigInteger 再相乘,最后转回字符串。

问题分析:
数据长度可达 200,超出 64 位整数范围;题目也禁止用大整数库或直接转整型。必须在字符/数组层面完成乘法。理想复杂度为 O(mn)(m、n 为两字符串长度),空间 O(m+n)

思路转折:
要想安全且高效 → 放弃内置数值运算 → 用“竖式乘法”:

  • 两两相乘,按位权落在正确的“结果槽位”上;

  • 把所有乘积先累加到对应槽位(相当于“卷积”);

  • 最后统一处理进位,再生成字符串。
    这样实现清晰、易于调试,也能稳定做到 O(mn)。

2.2 解法

算法思想:算法思想:
令 a = reverse(num1)、b = reverse(num2)(把最低位放前面),准备长度为 m+n-1 的整型数组 tmp 用于“无进位卷积”:

mp[i+j]+=(a[i]−′0′)⋅(b[j]−′0′)

然后从低到高扫描 tmp:

sum←carry+tmp[cur],输出(sum%10),carry←⌊sum/10⌋

若扫描完仍有 carry,继续“吐出”高位。最后反转得到正常顺序,并去掉可能出现的前导零。

i)特判:若任一输入为 "0",直接返回 "0"

ii)反转两个字符串并转为 char[]。

iii)申请int[] tmp(长度 m+n-1),双重循环累加 tmp[i+j] += (a[i]-'0')*(b[j]-'0')。

iiii)统一进位:用一个移动的 carry 从 tmp[0] 扫到 tmp[len-1],每步输出当前个位、更新进位;若 carry 仍非 0,继续输出直到为 0。

iiiii)删除将来会变成前导零的尾部 0(因为此时字符串是“低位在前”),再反转得到答案。

易错点:

  • 卷积阶段必须是 +=,不能写成 =(会覆盖之前累加)。

  • 记得把字符转数字:ch - '0'。

  • 结果桶长度为 m+n-1(卷积长度);统一进位阶段会把最高位的进位继续“吐出来”(所以最终位数≤m+n)。

  • 统一进位的循环条件建议写成 while (cur < tmp.length || carry != 0);sum 初始化为 carry,再按需加 tmp[cur]。

  • 去“前导零”的时机:在反转之前删尾部 0(至少保留 1 位,避免把 0 本身清空)。

2.3 代码实现

class Solution {public String multiply(String num1, String num2) {// 1) 边界:任一为 "0"if (num1.equals("0") || num2.equals("0")) return "0";int m = num1.length(), n = num2.length();char[] a = new StringBuilder(num1).reverse().toString().toCharArray();char[] b = new StringBuilder(num2).reverse().toString().toCharArray();// 2) 无进位卷积:长度 m + n - 1int[] tmp = new int[m + n - 1];for (int i = 0; i < m; i++) {int di = a[i] - '0';for (int j = 0; j < n; j++) {int dj = b[j] - '0';tmp[i + j] += di * dj; // 累加!!不是覆盖}}// 3) 统一进位:从低到高,把每格拆成 个位(输出) + 进位(带走)StringBuilder sb = new StringBuilder(m + n);int cur = 0, carry = 0;while (cur < tmp.length || carry != 0) {if (cur < tmp.length) carry += tmp[cur++];sb.append((char) ('0' + (carry % 10)));carry /= 10;}// 4) 去掉将来会成为“前导零”的尾部 0(至少保留1位)while (sb.length() > 1 && sb.charAt(sb.length() - 1) == '0') {sb.deleteCharAt(sb.length() - 1);}// 5) 反转得到正常顺序return sb.reverse().toString();}
}

复杂度分析:

  • 时间:O(mn),双重循环累积卷积项,统一进位线性扫描一次。

  • 空间:O(m+n),tmp 与输出字符串(最坏 m+n 位)。

http://www.xdnf.cn/news/20275.html

相关文章:

  • 【目录-多选】鸿蒙HarmonyOS开发者基础
  • 分布式go项目-搭建监控和追踪方案
  • 国内外支持个人开发者的应用市场
  • OpenCV - 图像的IO操作
  • 【开题答辩全过程】以 住院管理系统为例,包含答辩的问题和答案
  • 从零开始的python学习——文件
  • C++ 面向对象编程:多态相关面试简答题
  • 444444
  • LeetCode - 1089. 复写零
  • MQTT 与 Java 框架集成:Spring Boot 实战(三)
  • RAG提示词分解
  • CentOS系统管理:useradd命令的全面解析
  • Vllm-0.10.1:通过vllm bench serve测试TTFT、TPOT、ITL、E2EL四个指标
  • 多线程任务执行窗体框架jjychengTaskWinForm
  • 浅析Linux内核scatter-gather list实现
  • SQL 实战指南:电商订单数据分析(订单 / 用户 / 商品表关联 + 统计需求)
  • WordPress过滤文章插入链接rel属性noopener noreferrer值
  • 开源与定制化对比:哪种在线教育系统源码更适合教育培训APP开发?
  • 企业微信智能表格高效使用指南
  • Kafka Exactly-Once 语义深度解析与性能优化实践指南
  • 串口发送数据
  • 如何离线安装 VirtualMachinePlatform
  • 基于STM32单片机的家庭医护血氧体温血压吃药监测APP系统
  • 万字长文详解 MyCat 分表分库:从 0 到 1 构建高可用订单系统
  • 能发弹幕的简单视频网站
  • 计算机网络:调制解调器
  • Docker-volume数据卷
  • 为什么固态硬盘断电后数据还能保存不丢失?
  • 【LeetCode热题100道笔记】二叉树展开为链表
  • 激光频率梳 3D 轮廓测量 - 油路板的凹槽深度和平面度测量