蓝桥杯练习day2

大整数乘法

思路一:大整数乘法

想了一会,没想到数学的解法,于是打算用大整数乘法做。
java是支持大整数乘法的,但是直接用就没意思了,起不到练习的效果,打算手动实现一下。


UML类图与流程图

UML类图

在这里插入图片描述

流程图


代码

SeqAddLayer 类
public class SeqAddLayer {
    private String res = null;

    public SeqAddLayer() {
    }

    public String getRes() {
        return res;
    }

    public void forward(String sy) {
        if (this.res == null) {
            this.res = sy;
            return;
        }
        char[] x = new StringBuilder(this.res).reverse().toString().toCharArray();
        char[] y = new StringBuilder(sy).reverse().toString().toCharArray();

        int i = 0, j = 0, step = 0;
        StringBuilder res = new StringBuilder();
        while (i < x.length || j < y.length) {
            int ix=0, iy=0, tmp=0;
            if (i < x.length) {
                ix = x[i] - '0';
                i++;
            }
            if (j < y.length) {
                iy = y[j] - '0';
                j++;
            }
            tmp = ix + iy + step;
            step = tmp / 10;
            res.append(tmp % 10);
        }
        if (step != 0)
            res.append(step);
        this.res = res.reverse().toString();
    }

    public void reset(){
        this.res = null;
    }
}

CharMulSeqLayer 类
public class CharMulSeqLayer {
    public char[] y;

    public CharMulSeqLayer(String y) {
        //翻转并转为字符数组
        this.y = new StringBuilder(y).reverse().toString().toCharArray();
    }

    public CharMulSeqLayer() {
    }

    public void setY(String y) {
        this.y = new StringBuilder(y).reverse().toString().toCharArray();
    }

    public String forward(char x) {
        int step = 0;
        StringBuilder res = new StringBuilder();
        for (char val : y) {
            int tmp = multiply(x, val, step);
            step = tmp / 10;
            res.append(tmp % 10);
        }
        if (step != 0)
            res.append(step);
        return res.reverse().toString();
    }

    public int multiply(char cx, char cy, int step) {
        int ix = cx - '0';
        int iy = cy - '0';
        return ix * iy + step;
    }
}


BigIntegerMul 类
public class BigIntegerMul {
    private final CharMulSeqLayer mulLayer = new CharMulSeqLayer();
    private final SeqAddLayer addLayer = new SeqAddLayer();
    private char[] x;

    public BigIntegerMul(String x, String y) {
        this.x = new StringBuilder(x).reverse().toString().toCharArray();
        mulLayer.setY(y);
    };

    public BigIntegerMul() {};

    public void setY(String y) {
        mulLayer.setY(y);
    }

    public void setX(String x) {
        this.x = new StringBuilder(x).reverse().toString().toCharArray();
    }

    public String forward() {
        addLayer.reset();
        for (int i = 0; i < x.length; i++) {
            String tmp = mulLayer.forward(x[i]);
            addLayer.forward(tmp + "0".repeat(i));//左移相加
        }
        return addLayer.getRes();
    }
}


Test 类
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        //接收数据
        Scanner scan = new Scanner(System.in);
        List<String> nums = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            nums.add(scan.next());
        }

        //计算大整数乘法
        BigIntegerMul bigIntegerMul = new BigIntegerMul();
        bigIntegerMul.setX("1");
        String res = "";
        for (String num : nums) {
            bigIntegerMul.setY(num);
            res = bigIntegerMul.forward();
            bigIntegerMul.setX(res);
            System.out.printf("num is %s, tmp is %s\n", num, res);
        }

        //统计0的个数
        int count = 0;
        for (int i = res.length() - 1; i >= 0; i--) {
            if (res.charAt(i) == '0')
                count++;
            else
                break;
        }
        System.out.println(count);
        scan.close();
    }
}

优化思路

接收数据进行连乘可以用分治优化一下,但是懒得写了,之后想写再写吧(大概是没以后了)。

时间复杂度分析

长度未m和n的两个字符串相乘,时间复杂度为: T ( n ) = O ( m n ) + O ( m ) = O ( m n ) T(n)=O(mn)+O(m)=O(mn) T(n)=O(mn)+O(m)=O(mn)
如果说给出k个数连乘,直接遍历的时间花费是: T ( k ) = O ( k m n ) T(k)=O(kmn) T(k)=O(kmn)

如果使用分治对字符串连乘,那么k个数可以形成一棵深度为logk的树,总时间花费是 T ( k ) = O ( l o g k ∗ m n ) T(k)=O(logk * mn) T(k)=O(logkmn)
对比两种方法,分治的m增长速度远小于直接连乘的m,所以分治优化的速度还是很大的。


思路二:相乘后统计尾0

两个数直接相乘,统计尾0后删除尾0,可以有效避免数据的增长。
但如果数据相乘后没有0,那就不能通过了。

可以使用分治优化相乘的过程,代码暂略。

总结

以前写过c++的大整数乘法,印象中很简单,这次写的好繁琐。
java写的一塌糊涂,写的是一次性代码,拓展性和维护性极差,有空再改改吧。

补充:43. 字符串相乘

在这里插入图片描述
补充一个以前写的c++代码

class Solution {
public:
    string multiply(string num1, string num2) {
        if (num1 == "0" || num2 == "0") return "0";
        int step = 0; //控制移位
        string res;
        for (int i = num2.size() - 1; i >= 0; i--) //模拟字符串乘法
        {
            string temp;
            int tag = 0, num = num2[i] - '0'; //控制进位
            for (int j = 0; j < step; j++) //追加0
                temp += '0';
            for (int j = num1.size() - 1; j >= 0; j--)
            {
                int n = (num1[j] - '0') * num + tag;
                temp += to_string(n % 10);
                tag = n / 10;
            }
            if (tag) temp += to_string(tag);
            reverse(temp.begin(), temp.end());
            res = addStrings(res, temp);
            step++;
        }
        return res;
    }

    string addStrings(string num1, string num2) { //模拟字符串加法
        int tag = 0, i = num1.size() - 1, j = num2.size() - 1;
        string res;
        while (i >= 0 || j >= 0)
        {
            int ans = i >= 0 ? num1[i--] - '0' : 0;
            ans += j >= 0? num2[j--] - '0' : 0;
            res += to_string((ans + tag) % 10);
            tag = (ans + tag) / 10;
        }
        if (tag)
            res += to_string(tag);
        reverse(res.begin(), res.end());
        return res;
    }
};

415. 字符串相加


后记

翻到了别人的代码,自己写的好蠢。贴过来有空再看

    public String multiply(String num1, String num2) {
        if("0".equals(num1) || "0".equals(num2)){
            return "0";
        }
        int len1=num1.length();
        int len2=num2.length();
        int[] ans=new int[len1+len2];
        for(int i=len1-1;i>=0;i--){
            int value1=num1.charAt(i)-'0';
            for(int j=len2-1;j>=0;j--){
                int value2=num2.charAt(j)-'0';
                int sum=ans[i+j+1]+value1*value2;
                ans[i+j+1]=sum%10;
                ans[i+j]+=sum/10;
            }
        }
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<ans.length;i++){
            if(i==0 && ans[i]==0){
                continue;
            }
            sb.append(ans[i]);
        }
        return sb.toString();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值