《算法零基础100讲》(第27讲) 字符串算法 - 高精度

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法零基础100讲》(第27讲) 字符串算法 - 高精度相关的知识,希望对你有一定的参考价值。

零、写在前面

  这是《算法零基础100讲》 专栏打卡学习的第二十七天了。
  有不少同学已经不甘心只刷题了,「 解题报告 」 也已经陆续安排上了。每天都会有五六篇高质量的 「 解题报告 」 被我 「 加精 」。如果觉得自己有能力的,也可以来 「 万人千题 」 社区发布你的 「 解题报告 」。千万级流量,你我共同拥有,当然,不忘初心才是最重要滴。

一、概念定义

  高精度,是指当一个数无法用程序给定的类型进行表示时,利用数组进行模拟运算的算法。例如,整数12345678912345678912345已经超过了 64 64 64 位整型,所以我们可以将它存储在数组中,如下:

i i i012345678910111213141516171819202122
a [ i ] a[i] a[i]12345678912345678912345

  然后就是利用数组来模拟各种运算了。这里只介绍加法和乘法,减法 和 加法类似,除法需要用到二分枚举,暂不介绍。

1、高精度加法

  我们把数字按照数位存储到字符串中,加法运算是每一位对应相加,所以当两个字符串长度不一致时,需要对两个字符串进行从后往前遍历相加,为了方便计算,我们首先需要将它逆序,然后就可以顺序从前往后相加了,相加完毕后,需要处理进位,然后再将数位转换成字符。
总结为五步:
  1)加数逆序;
  2)模拟加法;
  3)处理进位;
  4)处理前导零;
  5)结果逆序;

char * addStrings(char * a, char * b){
    char *c;
    int lena = strlen(a);
    int lenb = strlen(b);
    int i, cap, now;
    int av, bv;
    int maxlen = max(lena, lenb);
    inverse(a);                           // (1)
    inverse(b);                           // (2)
    c = (char *)malloc( sizeof(char) * (maxlen + 2) );
    cap = 0;
    for(i = 0; i < maxlen; ++i) {
        av = (i < lena) ? (a[i]-'0') : 0; // (3)
        bv = (i < lenb) ? (b[i]-'0') : 0; // (4)
        now = (av + bv + cap);            // (5)
        cap = now / 10;                   // (6)
        c[i] = (now % 10) + '0';          // (7)
    }
    if(cap) {
        c[i] = '1';                       // (8)
        maxlen ++;
    }
    c[maxlen] = '\\0';
    inverse(c);                           // (9)
    return c;
}
  • ( 1 ) − ( 2 ) (1) - (2) (1)(2) 分别对两个加数进行逆序存储;
  • ( 3 ) (3) (3) 第一个加数转换成整数的形式,高位不够补0;
  • ( 4 ) (4) (4) 第二个加数转换成整数的形式,高位不够补0;
  • ( 5 ) (5) (5) 两个加数相加,并且带上前一位的进位;
  • ( 6 ) (6) (6) 处理当前位的进位;
  • ( 7 ) (7) (7) 将数位转换成字符;
  • ( 8 ) (8) (8) 处理加法后产生的最高位的进位;
  • ( 9 ) (9) (9) 最后,再进行一次逆序,高位和低位互换;

2、高精度乘法

  乘法和加法类似,也是采用字符串存储,首先将两个乘数的数位进行逆序,根据乘法的定义,第 i i i 位 和 第 j j j 位相乘以后,作用的位是在 i + j i+j i+j 位上,直接累加即可,最后再统一处理进位,消除前导零,并且处理正好是零的情况。总结为五步:
  1)乘数逆序;
  2)模拟乘法;
  3)处理进位;
  4)处理前导零;
  5)结果逆序;

char * multiply(char * a, char * b){
    int i, j, num;
    int lena = strlen(a);
    int lenb = strlen(b);
    int len = lena + lenb;
    int *cnum = (int *)malloc( sizeof(int) * (len+5) );
    char *c = (char *)malloc( sizeof(char) * (len+5) );
    memset( cnum, 0, sizeof(int) * (len+5) );
    inverse(a);                      // (1)
    inverse(b);                      // (2)
    for(i = 0; i < lena; ++i) {
        for(j = 0; j < lenb; ++j) {  // (3)
            cnum[i+j] += (a[i] - '0') * (b[j] - '0');
        }
    }
    for(i = 0; i < len; ++i) {       // (4)
        cnum[i+1] += cnum[i] / 10;
        cnum[i] %= 10;
    }
    ++len;
    while(len > 1 && !cnum[len-1]) { // (5)
        --len;
    }
    for(i = 0; i < len; ++i) {       // (6)
        c[i] = cnum[i] + '0';
    }
    c[len] = '\\0';
    inverse(c);                      // (7)
    return c;
}
  • ( 1 ) − ( 2 ) (1) - (2) (1)(2) 分别对两个乘数进行逆序存储;
  • ( 3 ) (3) (3) 模拟乘法;
  • ( 4 ) (4) (4) 处理进位;
  • ( 5 ) (5) (5) 处理前导零;
  • ( 6 ) (6) (6) 转换成字符;
  • ( 7 ) (7) (7) 再进行一次逆序,高位和低位互换;

二、题目描述

  给定两个二进制字符串,返回它们的和(用二进制表示)。输入为 非空 字符串且只包含数字 1 和 0。

三、算法详解

  模拟高精度加法,进位时进行模二处理即可。

四、源码剖析

void swap(char *a, char *b) {          
    char tmp = *a;
    *a = *b;
    *b = tmp;
}

int max(int a, int b) {
    return a > b ? a : b;
}

void inverse(char *a) {                // (1)
    int i, len = strlen(a);
    for(i = 0; i < len/2; ++i) {
        swap(&a[i], &a[len-1-i]);
    }
}

char * addBinary(char *a, char *b){    // (2)
    char *c;
    int lena = strlen(a);
    int lenb = strlen(b);
    int i, cap, now;
    int av, bv;
    int maxlen = max(lena, lenb);
    inverse(a);
    inverse(b);
    c = (char *)malloc( sizeof(char) * (maxlen + 2) );
    cap = 0;
    for(i = 0; i < maxlen; ++i) {
        av = (i < lena) ? (a[i]-'0') : 0;
        bv = (i < lenb) ? (b[i]-'0') : 0;
        now = (av + bv + cap);
        cap = now / 2;
        c[i] = (now % 2) + '0';
    }
    if(cap) {
        c[i] = '1';
        maxlen ++;
    }
    c[maxlen] = '\\0';
    inverse(c);
    return c;
}
  • ( 1 ) (1) (1) 实现字符串逆序;
  • ( 2 ) (2) (2) 类似上文提到的高精度加法;

五、推荐专栏

🧡《C语言入门100例》🧡

字符串翻转

六、习题练习

序号题目链接难度
1千位分隔数★☆☆☆☆
2字符串转化后的各位数字之和★☆☆☆☆
3字符串中第二大的数字★☆☆☆☆
4最小时间差★☆☆☆☆
5罗马数字转整数★★☆☆☆
6整数转罗马数字★★☆☆☆
7字符串压缩★★☆☆☆
8字符串相加★★★☆☆
9二进制求和★★★☆☆
10检查某单词是否等于两单词之和★★★☆☆
11字符串相乘★★★☆☆
12数字转英文★★★☆☆
👇🏻 关注公众号 观看 精彩学习视频👇🏻

以上是关于《算法零基础100讲》(第27讲) 字符串算法 - 高精度的主要内容,如果未能解决你的问题,请参考以下文章

《算法零基础100讲》(第26讲) 字符串算法 - 回文串

《算法零基础100讲》(第25讲) 字符串算法 - 字符串反转

《算法零基础100讲》(第24讲) 字符串算法 - 字符计数法

题解《算法零基础100讲》(第26讲) 字符串算法 - 回文串(java版)

题解《算法零基础100讲》(第22讲) 字符串算法 - 字符串比较(java版)

题解《算法零基础100讲》(第24讲) 字符串算法 - 字符计数法(java版)