[解题报告]《算法零基础100讲》(第27讲) 字符串算法 - 高精度

Posted XingleiGao

tags:

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

目录

零、写在前面

一、主要知识点

   1.高精度加法

   2.高精度乘法

   3.分治思想

二、课后习题 

1556. 千位分隔数

1945. 字符串转化后的各位数字之和

1796. 字符串中第二大的数字

539. 最小时间差

3. 罗马数字转整数

12. 整数转罗马数字

面试题 01.06. 字符串压缩

415. 字符串相加

67. 二进制求和

1880. 检查某单词是否等于两单词之和

43. 字符串相乘

273. 整数转换英文表示

写在最后


零、写在前面

        今天是打卡的第26天,今天稍微调整了点作息好很多了,大家就算要熬夜一定要两点前睡呀,保持六个小时充足睡眠。老规矩,知识点在:

《算法零基础100讲》(第27讲) 字符串算法(七) - 高精度https://blog.csdn.net/WhereIsHeroFrom/article/details/121347597


一、主要知识点

   1.高精度加法

  1. 加数逆序
  2. 模拟加法
  3. 处理进位
  4. 处理前导0
  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);                           // 1.
    c = (char *)malloc( sizeof(char) * (maxlen + 2) );
    cap = 0;
    for(i = 0; i < maxlen; ++i) {
        av = (i < lena) ? (a[i]-'0') : 0; // 超出范围设置为0
        bv = (i < lenb) ? (b[i]-'0') : 0; // 超出范围设置为0
        now = (av + bv + cap);            // 计算结果
        cap = now / 10;                   // 计算进位
        c[i] = (now % 10) + '0';          // 写入结果
    }
    if(cap) {
        c[i] = '1';                       // 进位 最多是1
        maxlen ++;
    }
    c[maxlen] = '\\0';
    inverse(c);                           // 反转
    return c;
}

   2.高精度乘法

  1. 乘数逆序
  2. 模拟乘法
  3. 处理进位
  4. 处理前导0
  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);                      // 反转
    inverse(b);                      // 反转
    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) {       // 处理进位
        cnum[i+1] += cnum[i] / 10;
        cnum[i] %= 10;
    }
    ++len;
    while(len > 1 && !cnum[len-1]) { // 处理前面的0
        --len;
    }
    for(i = 0; i < len; ++i) {       // 写入字符串
        c[i] = cnum[i] + '0';
    }
    c[len] = '\\0';
    inverse(c);                      // 反转
    return c;
}

   3.分治思想

当一个问题过于复杂的时候,先化成多个小问题,然后各个击破。

我就先提出来个概念,等下最后一题的时候我们用用-.-


二、课后习题 

1556. 千位分隔数

1556. 千位分隔数https://leetcode-cn.com/problems/thousand-separator/

题目描述

给你一个整数 n,请你每隔三位添加点(即 "." 符号)作为千位分隔符,并将结果以字符串格式返回。

思路

利用栈的先入后出的特性进行输入数字的反转就好了。加入分隔符。

char * thousandSeparator(int n){
    int zhan[13],zhansize = 0,ansSize = 0;//创建栈 先入后出
    while(n){
        zhan[zhansize++] = n % 10;  //栈赋值
        n /= 10;
    }
    if(!zhansize) zhan[zhansize++] = 0;
    char *ans = malloc(sizeof(char) * (zhansize + zhansize / 3 + 1));//返回字符串空间
    int j = zhansize;
    for(int i = 0;i <zhansize;i++){ //写入字符串信息
        if(i&&!(j%3))   ans[ansSize++] = '.';//够三位就加一个分隔符
        ans[ansSize++] = zhan[--j]+ '0' ;//出栈元素
    }
    ans[ansSize] = 0;
    return ans;
}

1945. 字符串转化后的各位数字之和

1945. 字符串转化后的各位数字之和https://leetcode-cn.com/problems/sum-of-digits-of-string-after-convert/

题目描述

给你一个由小写字母组成的字符串 s ,以及一个整数 k 。

首先,用字母在字母表中的位置替换该字母,将 s 转化 为一个整数(也就是,'a' 用 1 替换,'b' 用 2 替换,... 'z' 用 26 替换)。接着,将整数 转换 为其 各位数字之和 。共重复 转换 操作 k 次 。

例如,如果 s = "zbax" 且 k = 2 ,那么执行下述步骤后得到的结果是整数 8 :

转化:"zbax" ➝ "(26)(2)(1)(24)" ➝ "262124" ➝ 262124
转换 #1:262124 ➝ 2 + 6 + 2 + 1 + 2 + 4 ➝ 17

思路

由于k>=1,那么至少要做一次加和,那我们第一次读入的时候就把这个做了,也方便我们之后进行运算。之后就是按要求进行位和计算就好了。

int getLucky(char * s, int k){
    int ans = 0,i = 0;
    k--;
    while(s[i]){    //计算位和
        int temp = 0;
        temp = s[i] - 'a' +1;
        if(temp/10) temp = temp /10+temp %10;   //计算位和 最多两位?
        ans += temp;
        i++;
    }
    while(k--){ //迭代计算位和
        int temp = ans;
        ans = 0;
        while(temp){
            ans += temp %10;
            temp /= 10;
        }
    }
    return ans;
}

1796. 字符串中第二大的数字

1796. 字符串中第二大的数字https://leetcode-cn.com/problems/second-largest-digit-in-a-string/

题目描述

给你一个混合字符串 s ,请你返回 s 中 第二大 的数字,如果不存在第二大的数字,请你返回 -1 。

思路

初始化两个变量,一个是第一大的数字,一个第二大的,初始化为-1.(为啥是-1?题目说的)

根据扫描到的元素进行变量的改变 最后返回就好了。

int secondHighest(char * s){
    int max = -1,max1 = -1;    //两个最大值和次大值
    for(int i = 0;s[i];i++)
        if(s[i]>='0'&&s[i]<='9'){    //扫描到数字
            int temp = s[i] - '0';
            if(temp>max){max1 = max;max = temp;}//比最大值大 更新最大值和次大值
            else if(temp < max && temp > max1)    max1 = temp;//比次大值大但是小于最大值,只更新次大值
        }
    return max1;
}

539. 最小时间差

539. 最小时间差https://leetcode-cn.com/problems/minimum-time-difference/

题目描述

给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。

思路

先将所有时间转换为分钟的表示,然后做排序,最小值只可能出现在排序后的前后两个元素,或者最前和最后的位置。

int cmp(int *a,int*b){//升序排列
    return *a > *b;
}
int findMinDifference(char ** timePoints, int timePointsSize){
    int hash[timePointsSize],hashSize = 0;
    for(int i = 0;i<timePointsSize;i++)     //转化为分钟
        hash[hashSize++] = (((timePoints[i][0] -'0') * 10) + timePoints[i][1] - '0') * 60 + (timePoints[i][3] - '0')*10 + timePoints[i][4]; 
    qsort(hash,hashSize,sizeof(int),cmp);//排序
    int min = 24*60+hash[0] - hash[hashSize - 1];//最小值赋值为最小和最大值的差 垮了一天
    for(int i = 1;i < hashSize;i++)
        if(min>hash[i]-hash[i-1])   min = hash[i] - hash[i-1];
    return min;
}

3. 罗马数字转整数

3. 罗马数字转整数https://leetcode-cn.com/problems/roman-to-integer/

题目描述

常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

思路

顺序读入,如果读到V、X、L、C、D、M扫描前面的元素是不是对应的可以放置左边的元素,进行统计和就好了。

int romanToInt(char * s){
    int ans = 0;
    for(int i = 0;s[i];i++){
        if(s[i] == 'I') ans += 1;
        else if(s[i] == 'X')
            if(i&&s[i-1]=='I')   ans += 8;//上次统计的时候多加了1 此时就少加
            else ans += 10;
        else if(s[i] == 'V')
            if(i&&s[i - 1] =='I')   ans += 3;
            else ans += 5;
        else if(s[i] == 'L')
            if(i&&s[i - 1] =='X')   ans += 30;
            else ans+= 50;
        else if(s[i] == 'C')
            if(i&&s[i - 1] == 'X') ans += 80;
            else ans += 100;
        else if(s[i] == 'D')
            if(i&&s[i-1] == 'C') ans+=300;
            else ans+= 500;
        else if(s[i] == 'M')
            if(i&&s[i-1] == 'C') ans += 800;
            else ans+= 1000;
    }   
    return ans;
}

12. 整数转罗马数字

12. 整数转罗马数字https://leetcode-cn.com/problems/integer-to-roman/

题目描述

给你一个整数,将其转为罗马数字。

思路

根据不同元素代表的值进行写入就好了?把左边放元素的看作一个整体就完事了 ,这题不难的。就是情况太多 很烦

char * intToRoman(int num){
    char *ans = malloc(sizeof(char)*1000);
    int anssize = 0;
    while(num/1000){
        num -=1000;
        ans[anssize++]='M';
    }
    while(num/900){
        num-=900;
        ans[anssize++] = 'C';ans[anssize++] = 'M';
    }
    while(num/500){
        num -= 500;
        ans[anssize++] = 'D';
    }
    while(num /400){
        num -= 400;
        ans[anssize++] = 'C';ans[anssize++] = 'D';
    }
    while(num/100){
        num -= 100;
        ans[anssize++] ='C';
    }
    while(num/90){
        num-=90;
        ans[anssize++] = 'X';ans[anssize++] = 'C';
    }
    while(num/50){
        num-=50;
        ans[anssize++] = 'L';
    }
    while(num/40){
        num-=40;
        ans[anssize++] = 'X';ans[anssize++] = 'L';
    }
    while(num/10){
        num -= 10;
        ans[anssize++] = 'X';
    }
    while(num/9){
        num-=9;
        ans[anssize++] = 'I';ans[anssize++] = 'X';
    }
    while(num/5){
        num-=5;
        ans[anssize++] ='V';
    }
    while(num/4){
        num-=4;
        ans[anssize++] = 'I';ans[anssize++] = 'V';
    }
    while(num){
        num --;
        ans[anssize++] = 'I';
    }
    ans[anssize++] = 0;
    return ans;
}

面试题 01.06. 字符串压缩

面试题 01.06. 字符串压缩https://leetcode-cn.com/problems/compress-string-lcci/

题目描述

字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。

思路

先按照要求进行压缩,然后判断返回值就好了。

char* compressString(char* S){
    int Szie = 0,anssize = 0;//字符串长度和返回长度
    char *ans = malloc(sizeof(char)*60000);    //返回空间
    while(S[Szie]){    //按字符进行判断
        int temp = 0,zhan[5],zhansize = 0;
        while(S[temp+Szie] == S[Szie]) temp++;    //计算元素个数
        ans[anssize++] = S[Szie];
        int temp2 = temp;    //保留元素个数
        while(temp){        //利用栈转化为字符串
            zhan[zhansize++] = temp % 10;
            temp /= 10;
        }
        while(zhansize) ans[anssize++] = zhan[--zhansize]+'0';//转化为字符
        Szie = temp2 + Szie;    //跳过重复元素
    }
    ans[anssize] = 0;
    if(anssize>=Szie)    return S;//判断长度
    return ans;
}

415. 字符串相加

415. 字符串相加https://leetcode-cn.com/problems/add-strings/

题目描述

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

思路

按照知识点进行计算就好了,我比较喜欢直接while几次,我相信你们能看懂。

void swap(char *a,char *b){
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
void rever(char *s){//反转字符串
    int len = strlen(s);
    for(int i = 0;i < len/2;i++)
        swap(&s[i],&s[len-1-i]);
}
char * addStrings(char * num1, char * num2){
    int size1 = strlen(num1),size2 = strlen(num2),jinwei = 0;
    char *ans = malloc(sizeof(char)*10020);
    int ansSize = 0;
    while(size1&&size2){    //两数相加
        size1--;size2--;
        ans[ansSize++] = (num1[size1] - '0' + num2[size2] -'0' + jinwei) % 10 + '0';
        jinwei = (num1[size1]-'0' + num2[size2]-'0' + jinwei) /10;
    }
    while(size1){   //一还长
        size1--;
        ans[ansSize++] = (num1[size1] - '0' + jinwei)%10 + '0';
        jinwei = (num1[size1] -'0'+ jinwei)/10;
    }
    while(size2){   //二还长
        size2--;
        ans[ansSize++] = (num2[size2] - '0' + jinwei)%10 +'0';
        //printf("%d ",ansSize);
        jinwei = (num2[size2] - '0'+ jinwei)/10;
    }
    while(jinwei){  //进位还有
        ans[ansSize++] = jinwei%10 + '0';
        jinwei/=10;
    }
    ans[ansSize] =0;
    rever(ans);
    return ans;
}

67. 二进制求和

67. 二进制求和https://leetcode-cn.com/problems/add-binary/

题目描述

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

思路

按照知识点进行计算就好了,不同的就是进位的计算方式,直接看吧。

void swap(char *a,char *b){
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
void rever(char *s){
    int len = strlen(s);
    for(int i = 0;i < len/2;i++)
        swap(&s[i],&s[len - 1 -i]);
}
char * addBinary(char * a, char * b){
    int jinwei = 0,anssize = 0,size1 = strlen(a),size2 = strlen(b) ;
    char *ans = malloc(sizeof(char)*10010);
    while(size1 && size2){
        size1--;size2--;
        ans[anssize++] = (jinwei +a[size1] - '0'+b[size2] - '0')%2 +'0';
        jinwei = (jinwei + a[size1]-'0' +b[size2]-'0')/2; 
    }
    while(size1){
        size1--;
        ans[anssize++] = (jinwei + a[size1]-'0')%2 +'0';
        jinwei = (jinwei +a[size1] - '0')/2;
    }
    while(size2){
        size2--;
        ans[anssize++] = (jinwei +b[size2]-'0')%2 +'0';
        jinwei = (jinwei +b[size2] -'0')/2;
    }
    while(jinwei){
        ans[anssize++] = jinwei%2 +'0';
        jinwei/=2;
    }
    ans[anssize] = 0;
    rever(ans);
    return ans;
}

1880. 检查某单词是否等于两单词之和

1880. 检查某单词是否等于两单词之和https://leetcode-cn.com/problems/check-if-word-equals-summation-of-two-words/

题目描述

给你三个字符串 firstWord、secondWord 和 targetWord ,每个字符串都由从 'a' 到 'j' (含 'a' 和 'j' )的小写英文字母组成。

如果 firstWord 和 secondWord 的 数值之和 等于 targetWord 的数值,返回 true ;否则,返回 false 。

思路

这个题给的范围是8个,那一个int不就存的下了?那直接转成int加看等不等不就完了?

int tonum(char *s){     //转换为数字方便计算
    int ans = 0;
    for(int i = 0;s[i];i++){
        ans*=10;
        ans += s[i] - 'a';
    }
    return ans;
}
bool isSumEqual(char * firstWord, char * secondWord, char * targetWord){
    return tonum(targetWord) == tonum(firstWord) +tonum(secondWord);
}

43. 字符串相乘

43. 字符串相乘https://leetcode-cn.com/problems/multiply-strings/

题目描述

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

思路

按照知识点进行计算就好了。

void swap(char *a,char *b){
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
int verse(char *s){        //逆序
    int len = strlen(s);
    for(int i = 0;i<len/2;i++)  swap(&s[i],&s[len - 1 - i]);
    return len;
}
char * multiply(char * num1, char * num2){
    int size1 = verse(num1),size2 = verse(num2);//反转字符串 顺带提取长度
    int *ans = malloc(sizeof(int)*(size1 + size2 + 1));
    memset(ans,0,sizeof(int)*(size1+size2+1));
    char *anschar = malloc(sizeof(char)*(size1 + size2 +5));
    for(int i = 0;i < size1;i++)
        for(int j = 0;j < size2;j++)
            ans[i+j] += (num1[i] - '0' )*( num2[j] - '0');
    int jinwei = 0,anssize = size1+size2-1;
    for(int i = 0;i < size1 +size2 - 1;i++){//处理进位
        printf("%d %d",ans[i],jinwei);
        anschar[i] = (jinwei+ans[i])%10 + '0';
        jinwei = (jinwei+ans[i])/10;
    }
    while(jinwei){
        anschar[anssize++] = jinwei%10 +'0';
        jinwei/=10; 
    }
    while(anssize > 1&&anschar[anssize-1] == '0') anssize--;    //处理头部0
    anschar[anssize++] = 0;
    anssize=verse(anschar);
    return anschar;
}

273. 整数转换英文表示

273. 整数转换英文表示https://leetcode-cn.com/problems/integer-to-english-words/

题目描述

将非负整数 num 转换为其对应的英文表示。

思路

(终于到这题了,哈哈哈),我知识点里只说了一句分治。那这题怎么分治呢?

英语的逗号为啥是三位一个呢?因为他们算是三位是一组。然后读完一组加个单位就好了。跟我们喜欢四位一组一样呀。所以我们就按照这个规律来嘛。

先处理掉三位以内咋读,然后连一起就完事了?

char single[10][10] = {"Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine"};
char teens[10][10] = {"Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen","Sixteen","Seventeen","Eighteen","Nineteen"};
char ty[10][10] = {" ","Ten","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"};
char hundered[4][10] ={"Hundred","Thousand","Million","Billion"};
char *qian(int);//声明有一个函数

char * numberToWords(int num){
    char *ans =malloc(sizeof(char)*200); //每位数字最多两个单词 一个单词5位左右,最多九位估计90个
    ans[0]= 0;
    if(!num){
        strcat(ans,single[0]);//返回0
        return ans;
    }
    for(int i = 3,wei = 1000000000;i>=0;i--,wei/=1000){
            int temp = num/wei;
            if(temp){
                strcat(ans,qian(temp));
                if(i){
                    strcat(ans,ty[0]);//插入空格
                    strcat(ans,hundered[i]);//插入位信息
                    if(num %= wei)  strcat(ans,ty[0]);//插入空格
                }
            }
            num %= wei;
    }
    return ans;
}

char *qian(int num){        //处理三位数
    char *ans = malloc(sizeof(char)*100);//4位最多也就40个吧
    ans[0] = 0;
    if(num/100){
        strcat(ans,single[num/100]);
        strcat(ans,ty[0]);//空格
        strcat(ans,hundered[0]);//百
        num %= 100;
        if(num) strcat(ans,ty[0]);//空格

    }
    if(num/10 > 1){ //teens
        strcat(ans,ty[num/10]);//插入十位
        num %= 10;
        if(num) strcat(ans,ty[0]);
    }
    if(num >=10){
        strcat(ans,teens[num-10]);
        return ans;
    }
    else if(num > 0){
        strcat(ans,single[num]);
        return ans;
    }
    return ans;
}

写在最后

今天题量有点大,有点吃不消了是不存在的。。。哈哈哈哈哈哈  爽  大家加油呀。

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

《算法零基础100讲》(第17讲) 线性枚举 - 最值算法

《算法零基础100讲》(第28讲) 递推问题

《算法零基础100讲》(第15讲) 二分快速幂

《算法零基础100讲》(第13讲) 最大公约数

《算法基础学习》进制转换(入门)

《算法零基础100讲》(第31讲) 多维枚举 - 入门