Leetcode--Java--306. 累加数

题目描述

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

一个有效的累加序列必须至少包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。
给定一个只包含数字 ‘0’-‘9’ 的字符串,编写一个算法来判断给定输入是否是累加数。
说明: 累加序列里的数不会以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。

样例描述

在这里插入图片描述

思路

方法一:字符串模拟 + 高精度加法 O(n^3)

  1. 枚举第一段的长度,枚举第二段的长度,看和是否匹配第三段的长度。枚举中不能枚举前导0的数。匹配的话不断的移动a,b,c。
    在这里插入图片描述
  2. java的substring是表示首尾索引,并且左闭右开。
  3. 初始让a=-1且每段都初始指向该段前一个数,是类似虚拟头结点的思想,为了让处理第一个数和后面保持一致。
  4. 高精度加法模板要背过。(记得先转化为stringbuilder逆序,也可以直接从后往前遍历)

方法二:回溯 + 剪枝

  1. 对字符串进行拆分,遍历所有可能。从第三个数字开始,判断是否满足等于前两个数的和,满足的话就拆分,再继续找下一个数。否则的话继续拼接构成新的数字。防止溢出,拆分的数用long。
  2. 如果第三个数字的和大于前两个数字和,就没必要继续下去,直接剪枝,因为往后只能越来越大。
  3. 拆分过程中要保证不能出现前导0,除了0本身以外。如果不是第一位就不能为0,否则直接剪枝。
  4. 回溯中记录前两个数和,上一个数的值,当前下标以及已经生成的数的个数。

代码

class Solution {
    
    

    public boolean isAdditiveNumber(String num) {
    
    
       int n = num.length();
       for (int i = 0; i < n; i ++ ) {
    
    
           //至少三个数,所以让j的一个数也在范围内
           for (int j = i + 1; j + 1 < n; j ++ ) {
    
    
                 //指向每一段的前一个数(也就是上一段的最后一个数)
                    int a = -1, b = i, c = j;
                //由于不知道判断多少段和,所以设置不断循环直到末尾或者匹配失败
                while (true) {
    
    
                    String s1 = num.substring(a + 1, b + 1);
                    String s2 = num.substring(b + 1, c + 1);
                    //高精度加法不能有前导0,有的话该段失败,直接break
                    //至少2个数,并且前一个是0 
                    if (s1.length() > 1 && s1.charAt(0) == '0') {
    
    
                        break;
                    }
                    if (s2.length() > 1 && s2.charAt(0) == '0') {
    
    
                        break;
                    }
                    //将前两段加起来
                    String s3 = add(s1, s2);
                    //只有加起来的结果长度没有越界,并且与后面第三段匹配时,才更新每段的索引
                    if (c + s3.length() < n && num.substring(c + 1, c + s3.length() + 1).equals(s3)) {
    
    
                        a = b;
                        b = c;
                        c = c + s3.length();
                        //如果第三段的前一个,也就是上一段的结尾已经到达了总的末尾 说明成功
                        if (c == n - 1) {
    
    
                            return true;
                        }
                    }   
                    else break;

                }
           }
       }
       //如果循环完没有成功,就失败了
       return false;
    }
    //高精度加法模板
    public String add(String a, String b) {
    
    
        //先逆序,保证先处理个位
        a = new StringBuilder(a).reverse().toString();
        b = new StringBuilder(b).reverse().toString();
         String res = "";
        //只要有一个字符串没处理完(没越界),或者还有进位就不断循环处理
        for (int i = 0, c = 0; i < a.length() || i < b.length() || c > 0; i ++ ) {
    
    
            //还没处
            if (i < a.length()) {
    
    
              c += a.charAt(i) - '0';
            }
            if (i < b.length()) {
    
    
                c += b.charAt(i) - '0';
            }
            //取模求该位的数位
            res += c % 10;
            c /= 10;
        }
        //最后要逆序
        return new StringBuilder(res).reverse().toString();
    }
}

方法二:回溯 + 剪枝

class Solution {
    
    
    String num;
    int n;
    public boolean isAdditiveNumber(String num) {
    
    
         this.num = num;
         this.n = num.length();
         return dfs(0, 0, 0, 0);
    }
    
    public boolean dfs(int index, long presum, long previous, int cnt) {
    
    
        //已经到达末尾
        if (index == n) {
    
    
           //如果生成的数大于等于3个 
           return cnt >= 3;
        }
         //当前数
            long val = 0;
        for (int i = index; i < n; i ++ ) {
    
    
            //如果有前导0,直接break
            if (i > index && num.charAt(index) == '0') {
    
    
                break;
            }
           
            val = val * 10 + (num.charAt(i) - '0');
            //如果已经生了两个数
            if (cnt >= 2) {
    
    
                //如果当前数(第三个数)小于前面两个数的和,就往后继续拼接,否则直接break
                if (val < presum) {
    
    
                    continue;
                }
                //比前面大,就break
                else if (val > presum) {
    
    
                    break;
                }
            } 
            
              //使用这个数向下面递归   都是true的话就返回
            if (dfs(i + 1, previous + val, val, cnt + 1)) {
    
    
                return true;
            }
 
        } 
        //当前分支失败
       return false;
    }
}

猜你喜欢

转载自blog.csdn.net/Sherlock_Obama/article/details/121672839