Leetcode:累加数

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/junbujianwpl/article/details/83311778

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

一个有效的累加序列必须至少包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。

给定一个只包含数字 '0'-'9' 的字符串,编写一个算法来判断给定输入是否是累加数。

说明: 累加序列里的数不会以 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

进阶:
你如何处理一个溢出的过大的整数输入?

 

思路

3个要点:

1、如何处理过大的溢出的数。

不用数的加法,直接用字符串进行相加运算,最后判断字符串是否相同。所以需要一个字符串相加的辅助函数。

2、如何递归

数字累加和等于后面一个数,看上去像是猆波那契数列,是可以用递归来实现的。也即需要将一个问题划分为一个子问题。

递归方法:

例如“ABCDEF”,假设A+B=C,第一次划分,A、B、CDEF,A+B!=CDEF,此时可从CDEF中找出是否有前A、B的和,如果有,也即C,那么前面就可以是符合条件的串,接下来就看B、C、DEF能不能拆成符合条件的划分,由于少了一个元素,而且将前2个元素也划分好了,也即我们的问题变成了一个子问题。如此就可以一直递归下去。

需要注意的是递归只是用来验证某种初始划分的基础上,继续对后续元素划分能否符合要求。所以,还需要一个初始划分的循环给递归一个输入状态。初始状态的集合要确保是全面的。

3、如何划分初始状态

将整个数组划分为3段,然后交给递归help函数去判断能否符合累加规律。等同于在数组中插入2个隔板将数组分成3段,共有C_{n-1}^2。用代码来实现,一个二重循环即可,循环的计数从[0,n-1),表示在当前元素后面插入隔板。内循环的起始值从外循环当前计数值加1开始。

代码如下:

        bool isAdditiveNumber(string num) {
            for(int i=0; i+2<=num.size(); i++){
                for(int j=i+1; j+2<=num.size(); j++){
                    if(check(num.substr(0,i+1), num.substr(i+1,j-i), num.substr(j+1))) return true;
                }
            }
            return false;
        }
        bool check(string num1, string num2, string num){
            if(num1.size()>1 && num1[0]=='0' || num2.size()>1 && num2[0]=='0') return false;
            string sum=add(num1, num2);
            if(num==sum) return true;
            if(num.size()<=sum.size() || sum.compare(num.substr(0,sum.size()))!=0) return false;
            //或者用下面这条替换上面语句
            //if(num.size()<=sum.size() || sum!=num.substr(0,sum.size())) return false;
            else return check(num2, sum, num.substr(sum.size()));
        } 
        string add(string n, string m){
            string res;
            int i=n.size()-1, j=m.size()-1, carry=0;
            while(i>=0 || j>=0){
                int sum=carry+(i>=0 ? (n[i--]-'0') : 0) + (j>=0?  (m[j--]-'0') : 0);
                res.push_back(sum%10+'0');
                carry=sum/10;
            }
            if(carry) res.push_back(carry+'0');
            reverse(res.begin(), res.end());
            return res;
        }

注意事项:
check函数中不对第3个字符串即num判断是否是以0开头,因为num可能是多个字符串,比如"000",人家想表达的是0+0=0,这个是不违反规则的。

注意 i+2<=num.size()与i<=nums.size()-2的区别。

无符号数减一个大于自身的数会翻转变成一个非常大的数,循环会越界,甚至死循环。

优化初始状态:

第3个串长度应该不小于前2个串。所以可以限定下前2个串的长度,修改计数器含义,令计数器为串1和串2长度,最小长度均为1,剩下的为串3元素。可以让串1长度小于等于数组长度一半,然后让串2长度小于等于剩下数组的一半。如果还要再优化,可能出现初始集合不全的情况。代码如下:

        bool isAdditiveNumber(string num) {
            for(int i=1; i<=num.size()/2; i++){
                for(int j=1; j<=(num.size()-i)/2; j++){
                    if(check(num.substr(0,i), num.substr(i,j), num.substr(i+j))) return true;
                }
            }
            return false;
        }

算法并非自己想出来。乃是leetcode大神的讨论与分享。优化版本的初始集是大神手笔。

链接参考

https://leetcode.com/problems/additive-number/discuss/75576/0ms-concise-C%2B%2B-solution-(perfectly-handles-the-follow-up-and-leading-0s)

 

展开阅读全文

没有更多推荐了,返回首页