ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeetCode/字符串相乘

2022-05-31 01:31:45  阅读:154  来源: 互联网

标签:10 string num2 int add 相乘 字符串 LeetCode num1


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

扩充(字符串相加)
class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.length() - 1, j = num2.length() - 1, add = 0;//这里是从后往前,不必要转过来再从前往后
        string ans = "";
        while (i >= 0 || j >= 0 || add != 0) {//没加完或者有进位继续循环
            int x = i >= 0 ? num1[i] - '0' : 0;//这里通过补0统一操作
            int y = j >= 0 ? num2[j] - '0' : 0;//这里通过补0统一操作
            int result = x + y + add;
            ans.push_back('0' + result % 10);//倒序插入、方便内存动态扩充
            add = result / 10;//判断是否进位
            i -= 1;//num1指针前移
            j -= 1;//num2指针前移
        }
        // 计算完以后的答案需要翻转过来
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

1. 方法一(直接错位拼接)

对于两字符串相乘,首先将问题拆分成,字符与字符串的乘积,后面对字符串拼接求和即可
接着继续把字符与字符串乘积拆分成,字符与字符的乘积,这里可以直接转换成整型计算,同时用可动态扩充的结构倒序存储
这样一趟就计算出一个字符与字符串的乘积,再将每个字符与字符串乘积错位拼接,这里直接拼接

直接错位拼接(有问题但找不出来?)
class Solution {
public:
    //问题关键在于怎么存储
string multiply(string num1, string num2) {
    if(num1=="0"||num2=="0") return "0";
    string cur;//存储一次运算得到乘积
    string sum = "";
    for (int i = 0; i < num2.size(); i++) {//从高位往低位求和,求和保证每次错开一位
        cur = times(num1, num2[i]);
        sum = link(cur, sum);//每次sum求和时后移一位错位求和,相当与乘10,也相当于补0
    }
    for (auto& x : sum) x = x + '0';//转换成字符
    reverse(sum.begin(), sum.end());
    return sum;
}

string times(string num, char x) {//单个字符与字符串相乘
    int n = num.size();
    string cur = "";
    int add = 0;
    for (int i = n - 1; i >= 0; i--) {
        int product = (x - '0') * (num[i] - '0') + add;
        cur.push_back(product % 10);
        add = product / 10;
    }
    if (add > 0) cur.push_back(add);//倒序存放乘得的大数
    return cur;//保留倒序,用于拼接
}
//错位拼接两个字符串
string link(string num1, string num2) {//一前一后错一位
    string sum;
    sum.push_back(num1[0]);//错位求和,num1第一位不用求
    bool flag = false;
    int i = 1;//由于是倒序,从前往后即可
    while (i < num1.size() && i - 1 < num2.size()) {
        int product = num1[i] + num2[i - 1] + flag;
        i = i + 1;
        sum.push_back(product % 10);
        if (product / 10 == 0) flag = false;//判断是否进位
        else flag = true;
    }
    //拼接剩余的
    while (i < num1.size()) {
        sum.push_back((num1[i] + flag) % 10);
        i = i + 1;
        if (flag && ((num1[i] + flag) / 10) == 0) flag = false;//只要一次没进位就不用进了
    }
    while (i - 1 < num2.size()) {
        sum.push_back((num2[i - 1]  + flag) % 10);
        i = i + 1;
        if (flag && ((num2[i - 1] + flag) / 10) == 0) flag = false;//只要一次没进位就不用了       
    }
    if (flag) sum.push_back(flag);
    return sum;
}
};

2. 方法二(补0错位求和)

先计算每个字符和整条字符串的乘积,注意进位,倒序放入,方便内存扩充,然后考虑如何将每个字符与字符串的乘积加起来,
这里是在遍历字符的时候,从后往前,所以一开始对用于存放结果的字符串补0即可,随着遍历趟数增加,补充0的数目也逐渐增加
补充的0逻辑上相当于放在了低位,实现累加时的错位

补0错位
class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0")
            return "0";//0乘任何数为0
        string ans = "0";//当前错位累加和,初始值为0
        int m = num1.size(), n = num2.size();
        for (int i = n - 1; i >= 0; i--) {//从num2最后一位开始,遍历一次是与num1全部相乘的一个结果
            string curr;//当前
            int add = 0;
            for (int j = n - 1; j > i; j--)//往低位补0实现错位,每遍历一次多补一个0
                curr.push_back(0);
            int y = num2.at(i) - '0';//从后往前取num2整型
            //遍历完即实现一个字符与字符串相乘
            for (int j = m - 1; j >= 0; j--) {//从后往前取num1,其运算逻辑本来也是从低位到高位
                int x = num1.at(j) - '0';//转换num1整型
                int product = x * y + add;//运算出一位相乘的结果
                curr.push_back(product % 10);//存储
                add = product / 10;//记录进位
            }
            //如果最后有进位,记得补上
            while (add != 0) {
                curr.push_back(add % 10);
                add /= 10;
            }
            reverse(curr.begin(), curr.end());//反转过来该求和,得到真正的一次结果
            for (auto &c : curr) {
                c += '0';}
            ans = addStrings(ans, curr);//字符串求和
        }
        return ans;
    }
    string addStrings(string &num1, string &num2)//实现字符串相加功能,倒序加再动态扩充,注意进位,实现见开头      
};

3. 方法三(直接存放)

num1[i]与num2[j]的乘积会在最后结果的num[i+j+1]位,所以直接将位与位的计算结果存储即可
先乘积求和存储,最后全部运算完再考虑进位问题,这里就需要再倒序了,因为内存已经确定

直接存放
class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0") return "0";//零与任何数乘积为0
        int m = num1.size(), n = num2.size();//记录两数长度
        auto ansArr = vector<int>(m + n);//直接分配数组空间
        //全遍历消耗O(m×n)时间复杂度
        for (int i = m - 1; i >= 0; i--) {//从后往前遍历num1中每一个数
            int x = num1.at(i) - '0';//转换成整形
            for (int j = n - 1; j >= 0; j--) {//从后往前遍历num2中每一个数
                int y = num2.at(j) - '0';//转换成整形
                ansArr[i + j + 1] += x * y;//记录乘积结果
            }
        }
        //再单独整理成只含0~9范围的数,前面遍历过程无法整理
        for (int i = m + n - 1; i > 0; i--) {
            ansArr[i - 1] += ansArr[i] / 10;//超出部分进一位
            ansArr[i] %= 10;//原位置保留小于10的数
        }
        int index = ansArr[0] == 0 ? 1 : 0;//记录数进位后最终截止位置
        string ans;
        while (index < m + n) {
            ans.push_back(ansArr[index]);
            index++;
        }
        for (auto &c: ans)  c += '0';
        return ans;
    }
};

4. 方法四(快速傅里叶卷积运算)

这里相当于是对方法三进行优化,方法三相当于求了两字符串乘积运算时,两字符串各位对影响最终结果每一位的乘积求和
ansArr[i + j + 1] = num1[i] * num2[j],多分配一位为了存储最后可能的进位

其实也就是Ci=∑akbi-k,实质上,乘法的结果序列c也就是序列a与序列b的卷积

标签:10,string,num2,int,add,相乘,字符串,LeetCode,num1
来源: https://www.cnblogs.com/929code/p/16328977.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有