高精度运算 66、67、415、43、306

66. 加一(简单)

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例 3:

输入:digits = [0]
输出:[1]

提示:

  • 1 <= digits.length <= 100
  • 0 <= digits[i] <= 9

解法一、模拟逐位

首先一百位数组上限,肯定不能转int或者long(转了再转回数组工作量也挺大的。。)

class Solution {
    public int[] plusOne(int[] digits) {
        boolean isNine = false;
        if(digits[0] == 9)isNine = true;//如果最高位是9
        int[] res = new int[digits.length + 1];//提前备好新的数组
        int count = 0;
        if(digits[digits.length-1] + 1 < 10){//最后一位提前处理
            digits[digits.length - 1] += 1;
            return digits;
        }
        for(int i = digits.length - 1;i >= 0;i--){
            if(count == 1 && digits[i] + 1 < 10){//如果上一位有进位且可以马上处理
                digits[i] += 1;
                return digits;
            }else{//有进位
                digits[i] = 0;
                count = 1;
                if(i == 0){
                    res[0] = 1;
                    return res;
                }
            }
            if(isNine)res[i+1] = digits[I];//新的数组存存
        }
        return digits;
    }
}

解法二、解法一优化

不是,哥们jpg你这样显得我很蠢

不进直接return。一旦进入下个for,那么一定是进位,省略了count判断。

只要不是连串9,中途一定可以成功return。如果是连串9,只需要一个空数组+第一位是1,如{9,9,9}进位是{1,0,0,0}

class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
            digits[i]++;
            digits[i] = digits[i] % 10;
            if (digits[i] != 0) return digits;
        }
        digits = new int[digits.length + 1];
        digits[0] = 1;
        return digits;
    }
}

作者:YHHZW
链接:https://leetcode.cn/problems/plus-one/solutions/4481/java-shu-xue-jie-ti-by-yhhzw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解法三、找到最长的后缀9

如{1,2,3,9,9}第一个不是9的数字是3,3加1,后面全部置0

如果循环结束还没return,那如下

class Solution {
    public int[] plusOne(int[] digits) {
        int n = digits.length;
        for (int i = n - 1; i >= 0; --i) {
            if (digits[i] != 9) {
                ++digits[i];
                for (int j = i + 1; j < n; ++j) {
                    digits[j] = 0;
                }
                return digits;
            }
        }

        // digits 中所有的元素均为 9
        int[] ans = new int[n + 1];
        ans[0] = 1;
        return ans;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/plus-one/solutions/1057162/jia-yi-by-leetcode-solution-2hor/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 
67. 二进制求和(简单)

给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。

示例 1:

输入:a = "11", b = "1"
输出:"100"

示例 2:

输入:a = "1010", b = "1011"
输出:"10101"

提示:

  • 1 <= a.length, b.length <= 104
  • a 和 b 仅由字符 '0' 或 '1' 组成
  • 字符串如果不是 "0" ,就不含前导零

解法一、 模拟遍历

遍历最长的那个字符串,然后逐位加减。好繁琐,下标纠结了半天,把一道简单题做得挺复杂的。。是真不怎么长考虑细节的那部分脑子

class Solution {
    public static String addBinary(String a, String b) {
        StringBuffer sb = new StringBuffer();
        int count = 0;
        int lenA = a.length();
        int lenB = b.length();
        int lenMax = Math.max(lenA,lenB);
        for(int i = 1;i <= lenMax;i++){
            int A = 0;
            int B = 0;
            if(i <= lenA)A = a.charAt(lenA - i)-'0';
            if(i <= lenB)B = b.charAt(lenB - i)-'0';
            sb.insert(0,(A + B + count) % 2);
            count = (A + B + count)/2;
        }
        if(count == 1)sb.insert(0,count);
        return sb.toString();
    }
}

解法二、解法一优化

其实和我的类似,但是用了三目,省略了两个变量新建与赋值。注意这里的append后面的处理是多余的,sb.append()本身是带int的处理的

class Solution {
    public String addBinary(String a, String b) {
        StringBuffer ans = new StringBuffer();

        int n = Math.max(a.length(), b.length()), carry = 0;
        for (int i = 0; i < n; ++i) {
            carry += i < a.length() ? (a.charAt(a.length() - 1 - i) - '0') : 0;
            carry += i < b.length() ? (b.charAt(b.length() - 1 - i) - '0') : 0;
            ans.append((char) (carry % 2 + '0'));
            carry /= 2;
        }

        if (carry > 0) {
            ans.append('1');
        }
        ans.reverse();

        return ans.toString();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/add-binary/solutions/299667/er-jin-zhi-qiu-he-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


415. 字符串相加(简单)

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

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

示例 1:

输入:num1 = "11", num2 = "123"
输出:"134"

示例 2:

输入:num1 = "456", num2 = "77"
输出:"533"

示例 3:

输入:num1 = "0", num2 = "0"
输出:"0"

提示:

  • 1 <= num1.length, num2.length <= 104
  • num1 和num2 都只包含数字 0-9
  • num1 和num2 都不包含任何前导零

解法一、模拟遍历

乌乌 学聪明惹(指把前面几道题的优化部分搬过来优化了,但时间复杂度还是惨不忍睹(

    public String addStrings(String num1, String num2) {
        StringBuffer sb = new StringBuffer();
        int carry = 0;
        int t = 0;
        int lenA = num1.length();
        int lenB = num2.length();
        int lenMax = Math.max(lenA,lenB);
        for(int i = 1;i <= lenMax;i++){
            t = carry;
            t += i<= lenA? num1.charAt(lenA-i)-'0':0;
            t += i<= lenB? num2.charAt(lenB-i)-'0':0;
            sb.insert(0,t % 10);
            carry = t >= 10 ? 1:0;
        }
        if(carry!=0)sb.insert(0,carry);
        return sb.toString();
    }

解法二、解法一优化

双指针遍历,for改while(确实判断>=0比写一堆方便,但我不太习惯while)

顺便一提这是1ms答案,而2ms答案是把||carry==1放在while里,相当于每次判断多判断一个。。确实只是最后一次需要多判断的事,真是一个细节不注意就会拖沓啊。

class Solution {
    public String addStrings(String num1, String num2) {
        int i=num1.length()-1;
        int j=num2.length()-1;
        int addSum;
        int carry=0;
        StringBuilder res=new StringBuilder();
        while(i>=0||j>=0){
            int n1=i<0?0:num1.charAt(i--)-'0';
            int n2=j<0?0:num2.charAt(j--)-'0';
            addSum=n1+n2+carry;//!!记得加上carry
            res.append(addSum%10);
            carry=addSum/10;
        }
        if(carry==1){
            res.append(1);//!!!!!!如果最后还有进位,高位补1
        }
        return res.reverse().toString();
    }
}

43. 字符串相乘(中等)

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

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。

解法一、按顺序相乘模拟

 num1作为基准,num2作为拆分,从头字符一直乘到尾字符然后相加,每加一次左移一位。计组学过乘法器,但是没好好学。。

class Solution {
    public static String multiply(String num1, String num2) {
        String res = "";
        if(num1.equals("0") || num2.equals("0"))return "0";
        for(int i = 0;i < num2.length();i++){
            String a = multy(num1,num2.charAt(i));
            res = addStrings(res,a);
            if(i!=num2.length()-1)res = res + "0";
        }
        return res;
    }
    public static String multy(String num1,char c){
        int carry = 0;
        StringBuffer sb = new StringBuffer();
        for(int i = num1.length()-1;i>=0;i--){
            int t = (num1.charAt(i)-'0') * (c -'0') + carry;
            sb.insert(0,t%10);
            carry = t / 10;
        }
        if(carry != 0)sb.insert(0,carry);
        return sb.toString();
    }
    public static String addStrings(String num1, String num2) {
        int i=num1.length()-1;
        int j=num2.length()-1;
        int addSum;
        int carry=0;
        StringBuilder res=new StringBuilder();
        while(i>=0||j>=0){
            int n1=i<0?0:num1.charAt(i--)-'0';
            int n2=j<0?0:num2.charAt(j--)-'0';
            addSum=n1+n2+carry;//!!记得加上carry
            res.append(addSum%10);
            carry=addSum/10;
        }
        if(carry==1){
            res.append(1);//!!!!!!如果最后还有进位,高位补1
        }
        return res.reverse().toString();
    }
}

 

 解法二、数组存储,按位相乘

对于len1,从最右侧开始往左遍历,拆解数字。内扣循环len2,从右往左拆解,存入ansArr[i+j+1]位,这里下标是人脑提前预算好的。然后对于ansArr进行处理,把进位塞进去。

对于m长度的num1和n长度的num2相乘,最后的结果可能是m+n或者m+n+1。因此最后开始处理时,需要判断,去掉前导0。

这是1ms的击败99%的做法。

class Solution {
    public String multiply(String num1, String num2) {
        if(num1.equals("0")||num2.equals("0")){
            return "0";
        }
        int m=num1.length(),n=num2.length();
        int[] ansArr=new int[m+n];
        for(int i=m-1;i>=0;i--){
            int x=num1.charAt(i)-'0';
            for(int j=n-1;j>=0;j--){
                int y=num2.charAt(j)-'0';
                ansArr[i+j+1]+=x*y;//先乘后变
            }
        }
        for(int i=m+n-1;i>0;i--){
            ansArr[i-1]+=ansArr[i]/10;
            ansArr[i]%=10;
        }
        int index=ansArr[0]==0?1:0;
        StringBuilder ans=new StringBuilder();
        while(m+n>index){
            ans.append(ansArr[index]);//为1则从1开始
            index++;
        }
        return ans.toString();
    }
}

 
306. 累加数(中等)☆

累加数 是一个字符串,组成它的数字可以形成累加序列。

一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和。

给你一个只包含数字 '0'-'9' 的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回 true;否则,返回 false 。

说明:累加序列里的数,除数字 0 之外,不会 以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。

示例 1:

输入:"112358"
输出:true 
解释:累加序列为: 1, 1, 2, 3, 5, 8 。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8

示例 2:

输入"199100199"
输出:true 
解释:累加序列为: 1, 99, 100, 199。1 + 99 = 100, 99 + 100 = 199

提示:

  • 1 <= num.length <= 35
  • num 仅由数字(0 - 9)组成

进阶:你计划如何处理由过大的整数输入导致的溢出?

解法一、DFS

其实没法做,用例到一半的时候数字溢出···就是递归然后不断切分,切出一个前缀,往后分割

public class Solution {
    public boolean isAdditiveNumber(String num) {
        return dfs(num, new ArrayList<>());
    }

    private boolean dfs(String num, List<Integer> path) {
        // 如果路径长度 >= 3,且当前数字不等于路径中倒数第二个和倒数第三个数字之和,则返回 false
        if (path.size() >= 3 && path.get(path.size() - 1) != path.get(path.size() - 2) + path.get(path.size() - 3)) {
            return false;
        }
        // 如果路径长度 >= 3,并且没有剩余的字符串,返回 true
        if (path.size() >= 3 && num.isEmpty()) {
            return true;
        }
        // 遍历所有可能的前缀
        for (int i = 1; i <= num.length(); i++) {
            String curr = num.substring(0, i);
            // 如果前缀长度大于 1 且以 0 开头,跳过
            if (curr.length() > 1 && curr.charAt(0) == '0') {
                continue;
            }
            // 将当前前缀转换为整数,并递归处理剩余字符串
            int currNum = Integer.parseInt(curr);
            if (dfs(num.substring(i), new ArrayList<>(path) {{add(currNum);}})) {
                return true;
            }
        }
        return false;
    }
}

解法二、也是分裂(双百解法)

第一个函数负责分裂,边界情况讨论。i是第二个数最后一个下标,j是分裂的位置,j+1是第二个数的首下标。backtrack是一个递归函数,num是原字符串,ij意义同上,idx是第三个数首下标。

isAdditiveNumber 方法

  1. 外层循环for (int i = 1; i < num.length(); i++)):

    • i 是第二个数的最后一个下标。
    • 循环控制第二个数的结束位置。
  2. 内层循环for (int j = 0; j < i; j++)):

    • j 是第一个数的最后一个下标。
    • 用于计算第一个数 k 的值。
  3. 计算第一个数k = k * 10 + (num.charAt(j) - '0')):

    • 将 num 的前 j 位字符转换为第一个数 k
  4. 处理前导零

    • 如果第一个数的长度大于1并且以0开头,则直接跳出内层循环。
    • 如果第二个数的长度大于1并且以0开头,则跳过此次分裂。
  5. 计算第二个数for (int m = j + 1; m <= i; m++)):

    • 将 num 从 j+1 位到 i 位的字符转换为第二个数 l
  6. 递归检查if (backtrack(num, k, l, i + 1)) return true):

    • 调用 backtrack 方法检查从 i + 1 位置开始是否能形成累加数列。

backtrack 方法

  1. 计算累加数

    • 将从 idx 开始的字符转换为一个数 n,并与前两个数 i 和 j 的和进行比较。
  2. 检查条件

    • 如果 i + j == n,且已经到达字符串末尾或递归调用返回 true,则返回 true
    • 如果 num 从 idx 位置开始以 0 开头或 n 大于或等于 i + j,则跳出循环。
  3. 递归调用

    • 如果 i + j == n,则递归调用 backtrack 方法,更新前两个数为 j 和 n,并从 k + 1 位置继续检查。
class Solution {
    public boolean isAdditiveNumber(String num) {
        for (int i = 1; i < num.length(); i++) {
            long k = 0;
            for (int j = 0; j < i; j++) { // k, l 分别代表两加数,j为分裂的位置
                k = k * 10 + (num.charAt(j) - '0'); // 计算第一个数的值
                long l = 0;
                if (j > 0 && num.charAt(0) == '0') break; // 如果第一个数开头是0并且此时长度大于1,则直接结束循环
                if (i - j - 1 > 0 && num.charAt(j + 1) == '0') continue; // 如果第二个数开头是0且长度大于1,则跳过此次分裂
                for (int m = j + 1; m <= i; m++) l = l * 10 + (num.charAt(m) - '0'); // 计算第二个数的值
                if (backtrack(num, k, l, i + 1)) return true; // 如果组成累加数,返回true
            }
        }
        return false;
    }

    public boolean backtrack(String num, long i, long j, int idx) {
        long n = 0;
        for (int k = idx; k < num.length(); k++) {
            n = n * 10 + (num.charAt(k) - '0'); // 计算两数可能的和
            if (i + j == n) {
                if (k == num.length() - 1 || backtrack(num, j, n, k + 1)) return true;
            }
            if (num.charAt(idx) == '0' || n >= i + j) break; // 如果第一个位置上为0或此时和大于两数之和,则退出循环,不用再继续累加和
        }
        return false;
    }
}

解法三、官方题解,通过1/2推后面,判断和原字符串是否相等(其实是暴力

第一个函数:负责分割出第一个、第二个数字,其中包含先导零则零。第二个函数:负责验证是否正确,根据一和二来创造一整个字符串,和原字符串比对。第三个函数:字符串加法,处理大型整数溢出。

class Solution {
    public boolean isAdditiveNumber(String num) {
        int n = num.length();
        for (int secondStart = 1; secondStart < n - 1; ++secondStart) {
            if (num.charAt(0) == '0' && secondStart != 1) {
                break;
            }
            for (int secondEnd = secondStart; secondEnd < n - 1; ++secondEnd) {
                if (num.charAt(secondStart) == '0' && secondStart != secondEnd) {
                    break;
                }
                if (valid(secondStart, secondEnd, num)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean valid(int secondStart, int secondEnd, String num) {
        int n = num.length();
        int firstStart = 0, firstEnd = secondStart - 1;
        while (secondEnd <= n - 1) {
            String third = stringAdd(num, firstStart, firstEnd, secondStart, secondEnd);
            int thirdStart = secondEnd + 1;
            int thirdEnd = secondEnd + third.length();
            if (thirdEnd >= n || !num.substring(thirdStart, thirdEnd + 1).equals(third)) {
                break;
            }
            if (thirdEnd == n - 1) {
                return true;
            }
            firstStart = secondStart;
            firstEnd = secondEnd;
            secondStart = thirdStart;
            secondEnd = thirdEnd;
        }
        return false;
    }

    public String stringAdd(String s, int firstStart, int firstEnd, int secondStart, int secondEnd) {
        StringBuffer third = new StringBuffer();
        int carry = 0, cur = 0;
        while (firstEnd >= firstStart || secondEnd >= secondStart || carry != 0) {
            cur = carry;
            if (firstEnd >= firstStart) {
                cur += s.charAt(firstEnd) - '0';
                --firstEnd;
            }
            if (secondEnd >= secondStart) {
                cur += s.charAt(secondEnd) - '0';
                --secondEnd;
            }
            carry = cur / 10;
            cur %= 10;
            third.append((char) (cur + '0'));
        }
        third.reverse();
        return third.toString();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/additive-number/solutions/1200446/lei-jia-shu-by-leetcode-solution-cadc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

碎碎念:

  • 实现了加法器,造了几个小破轮子。字符串加加加乘乘乘,略有复杂。。三目和边界判断很好玩
  • 66、67、415难度逐步递加。造成了43一个解法的基础。306想不到暴力的话是真有点难。415其实是后面两道题的一个基础。
  • 累加数的DFS解法也是真的挺神奇的,除了大数溢出了
  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值