刷题笔记:力扣第43、67题(字符串计算)

力扣第43题-字符串相乘

1.有了过往题目的经验,直接可以舍弃掉"先将字符转换为数字,相乘得出结果后转换成字符串"的错误想法,因为给定的两个字符串长度最长可到200,相乘一定会溢出。

2.想到可以用小学列竖式计算的方式来进行计算,每次只取其中一位进行相乘计算,得出的结果只会影响当前位和下一位(进位)。所以可以使用两个for循环来遍历两个字符串的每一位,每次内层循环开始的时候初始化进位值carry为0,并初始化当前指针指向外层循环字符的所在位。执行过程中处理相乘的结果,加上进位值后得出临时值tmp,对10整除后填入carry,而在确定下一位的值时还需要判断本位加上tmp对10取余的值mul后是否还小于10,如果小于10则直接填入本位,如果大于等于10需要再次对10取余后填入本位,同时让carry加上mul对10整除的值。

3.当一次内层循环结束后,还需要判断进位值carry是否为0,如果不为0还需要填入下一位。当外层循环也结束后,当前cur的值一定等于数字实际长度,可以根据cur进行结果字符串的内存申请,同时逆序遍历数组依次转换为字符填入结果字符串即可。

4.基于以上代码逻辑,可写出完整代码如下:

cpp 复制代码
 1. char* multiply(char* num1, char* num2) {
 2.     int len1 = strlen(num1);  // 数字1的长度
 3.     int len2 = strlen(num2);  // 数字2的长度
 4.     // 乘积的最大长度 = len1 + len2(比如 99*99=9801,2+2=4)
 5.     int nums[len1 + len2];
 6.     // 初始化数组为0,避免脏数据
 7.     memset(nums, 0, sizeof(int) * (len1 + len2));
 8.     int cur, carry;  // cur:当前要填充的位置;carry:进位
 9.  
10.     // 特判:如果任一数是"0",直接返回"0"(避免后续无效计算)
11.     if ((len1 == 1 && num1[0] == '0') || (len2 == 1 && num2[0] == '0')){
12.         char* res = (char*)malloc(sizeof(char) * 2);
13.         res[0] = '0';
14.         res[1] = '\0';
15.         return res;
16.     }
17.  
18.     // ===================== 核心:模拟竖式乘法 =====================
19.     // 从右往左遍历num1(个位开始乘)
20.     for (int i = len1 - 1; i >= 0; i--){
21.         // cur 初始为 len1-1-i:表示当前num1位对应的结果低位偏移(比如i=len1-1 → cur=0,对应个位)
22.         cur = len1 - 1 - i;
23.         carry = 0;  // 每次乘新的num1位时,进位重置为0
24.         // 从右往左遍历num2
25.         for (int j = len2 - 1; j >= 0; j--){
26.             // 计算当前位乘积 + 之前的进位
27.             int tmp = (num1[i] - '0') * (num2[j] - '0') + carry;
28.             // 当前结果位 = 原数组值 + 乘积的个位
29.             int mul = nums[cur] + tmp % 10;
30.             // 新的进位 = 乘积的十位
31.             carry = tmp / 10;
32.  
33.             if (mul < 10){
34.                 // 无进位,直接赋值
35.                 nums[cur++] = mul;
36.             } else {
37.                 // 有进位,加到carry里,当前位取个位
38.                 carry += mul / 10;
39.                 nums[cur++] = mul % 10;
40.             }
41.         }
42.  
43.         // 内层循环结束后,如果还有进位,加到结果的下一位
44.         if (carry != 0){
45.             nums[cur++] += carry;
46.         }
47.     }
48.  
49.     // ===================== 转换为字符串 =====================
50.     // 分配结果字符串内存(cur是有效位数,+1存\0)
51.     char* res = (char*)malloc(sizeof(char) * (cur + 1));
52.     res[cur] = '\0';  // 字符串结束符
53.     // 逆序遍历nums数组(因为我们是从低位往高位存的),转成字符
54.     for (int i = cur - 1; i >= 0; i--){
55.         res[i] = nums[cur - 1 - i] + '0';
56.     }
57.  
58.     return res;
59. }

该算法的时间复杂度为O(len1×len2),空间复杂度为O(len1+len2),已是本题标准解。

5.值得注意的是,本题最特殊的情况应该是提供的两个字符串有至少一个为'0',如果不进行处理会导致最后输出的字符串是类似'000000'的形式,所以要单独进行这种特殊情况的判断。

力扣第67题-二进制求和

1.本题和力扣第2题-两数相加很相似,区别是本题是二进制计算,但思路都一样。使用一个整形数组来逆序记录每一位的值,然后使用两个指针分别从两个字符串的末尾开始遍历、相加,中间需要注意处理进位,以及处理至少其中一个字符串为'0'的特殊情况。最后给结果字符串开辟内存空间,遍历整形数组来为结果字符串幅值即可。

2.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. char* addBinary(char* a, char* b) {
 2.     // 1. 获取两个字符串长度
 3.     int len1 = strlen(a);
 4.     int len2 = strlen(b);
 5.  
 6.     // 2. 结果最大长度 = 较长的长度 + 1(最高位可能进位)
 7.     int numsLen = fmax(len1, len2) + 1;
 8.     int nums[numsLen];  // 存储每一位计算结果(低位在前)
 9.  
10.     // 3. 特判:如果其中一个是 0,直接返回另一个
11.     if (b[0] == '0'){
12.         return a;
13.     }
14.     if (a[0] == '0'){
15.         return b;
16.     }
17.  
18.     // 4. 指针从末尾开始(个位开始加)
19.     int i = len1 - 1;
20.     int j = len2 - 1;
21.     int cur = 0;      // 结果数组当前位置
22.     int isTwo = 0;    // 进位值(0 或 1)
23.  
24.     // ===================== 核心加法 =====================
25.     // 5. 两个数都还有位:一起加
26.     while (i >= 0 && j >= 0){
27.         // 计算当前位总和:a位 + b位 + 进位
28.         int tmp = a[i] + b[j] - 2 * '0' + isTwo;
29.         isTwo = tmp / 2;    // 新进位:逢2进1
30.         nums[cur++] = tmp % 2; // 当前位保留 0 或 1
31.         i--;
32.         j--;
33.     }
34.  
35.     // 6. a 还有剩余位(b已用完)
36.     while (i >= 0){
37.         int tmp = a[i] - '0' + isTwo;
38.         isTwo = tmp / 2;
39.         nums[cur++] = tmp % 2;
40.         i--;
41.     }
42.  
43.     // 7. b 还有剩余位(a已用完)
44.     while (j >= 0){
45.         int tmp = b[j] - '0' + isTwo;
46.         isTwo = tmp / 2;
47.         nums[cur++] = tmp % 2;
48.         j--;
49.     }
50.  
51.     // 8. 最后还有进位,必须加上
52.     if (isTwo != 0){
53.         nums[cur++] = isTwo;
54.     }
55.  
56.     // ===================== 转成字符串 =====================
57.     // 9. 开辟结果内存
58.     char* res = (char*)malloc(sizeof(char) * (cur + 1));
59.     res[cur] = '\0';
60.  
61.     // 10. 逆序:nums 低位在前 → 转成高位在前的字符串
62.     for (int k = 0; k < cur; k++){
63.         res[k] = nums[cur - 1 - k] + '0';
64.     }
65.  
66.     return res;
67. }

该算法时间复杂度和空间复杂度均为O(max(len(a), len(b))),是本题的标准解。

3.力扣上有更加精简的代码版本如下:

cpp 复制代码
 1. char* addBinary(char* a, char* b) {
 2.     int alen = strlen(a);        // 字符串 a 的长度
 3.     int blen = strlen(b);        // 字符串 b 的长度
 4.     // 开辟结果数组:最长可能长度 = 较长长度 + 2(进位 + \0)
 5.     // calloc 自动全部初始化为 0,自带结束符
 6.     char* ans = (char*)calloc(fmax(alen, blen) + 2, sizeof(char));
 7.  
 8.     int ap = alen - 1;           // a 的指针:从最后一位(个位)开始
 9.     int bp = blen - 1;           // b 的指针:从最后一位(个位)开始
10.     int p = 0;                   // 结果数组的下标(从 0 开始存)
11.     int temp = 0;                // 进位(0 或 1)
12.  
13.     // 核心循环:只要 a 没加完 或 b 没加完,就继续加
14.     while (ap >= 0 || bp >= 0) {
15.         // 三数求和:a当前位 + b当前位 + 进位
16.         int sum =
17.             (ap >= 0 ? a[ap] - '0' : 0) +  // a 还有位就取,没位补 0
18.             (bp >= 0 ? b[bp] - '0' : 0) +  // b 还有位就取,没位补 0
19.             temp;                          // 加上进位
20.  
21.         // 判断是否进位
22.         if (sum > 1) {
23.             sum %= 2;     // 当前位只保留 0 或 1
24.             temp = 1;     // 进位 = 1
25.         } else {
26.             temp = 0;     // 不进位
27.         }
28.  
29.         ans[p++] = '0' + sum;  // 把数字转字符存入结果数组
30.         --ap;                  // a 指针左移
31.         --bp;                  // b 指针左移
32.     }
33.  
34.     // 最后如果还有进位,必须加 1
35.     if (temp)
36.         ans[p++] = '1';
37.  
38.     // ===================== 关键:翻转字符串 =====================
39.     // 因为我们是 低位先存 → 数组顺序是反的 → 必须翻转
40.     int l = 0;
41.     int r = strlen(ans) - 1;
42.     while (l < r) {
43.         char c = ans[l];
44.         ans[l] = ans[r];
45.         ans[r] = c;
46.         ++l;
47.         --r;
48.     }
49.  
50.     return ans;  // 返回正确顺序的结果
51. }

该算法有如下几点优化:

①使用一个三目运算代替了我的代码中的三段循环代码:

cpp 复制代码
1. int sum=(ap>=0?a[ap]-'0':0)+(bp>=0?b[bp]-'0':0)+temp;

②不需要使用额外的整形数组来逆序记录每一位的值,而是直接将答案先逆序存到结果字符串中,最后遍历翻转即可。

相关推荐
yang_B6212 小时前
最小二乘法 拟合平面
算法·平面·最小二乘法
放下华子我只抽RuiKe52 小时前
深度学习全景指南:硬核实战版
人工智能·深度学习·神经网络·算法·机器学习·自然语言处理·数据挖掘
多看书少吃饭2 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记
吴秋霖3 小时前
【某音电商】protobuf聊天协议逆向
python·算法·protobuf
了一梨3 小时前
[T113] 交叉编译 OpenCV 4.5.2 + face 模块
linux·笔记·opencv
m0_587958953 小时前
C++中的命令模式变体
开发语言·c++·算法
似水এ᭄往昔3 小时前
【数据结构】--链表OJ
数据结构·算法·链表
困死,根本不会3 小时前
VMware Ubuntu 显示有线连接却无法上网|完整排查与解决笔记
linux·笔记·ubuntu
2501_924952693 小时前
代码生成器优化策略
开发语言·c++·算法