Digit Divide Numbers

Digit Divide Numbers

这是一道笔者纠结了很久的题,Lintcode编号742,经过一系列处理终于通过了测试,也算完成了一项小小的心愿,在这里就分享下解题的过程和思路。

已知

Digit Divide Numbers 是这样一类数,以128举例,128能整除128,所以这个数符合要求,也就是说一个数能被自身所有整数整除即可。
限定:0<=L<=R<=2^31-1,R-L<=10^6

第一回合:这题乍一看真心简单,写一个判定函数,然后从lower开始一直遍历到upper就好了,这部分代码着实易写:

    public static List<Integer> digitDivideNums(int lower, int upper) {
        List<Integer> ret = new ArrayList<Integer>();
        for (int i = lower; i <= upper; i++) {
            if(isDigitDivideNum(i)){
                ret.add(i);
            }
        }
        return ret;
    }

    private static boolean isDigitDivideNum(int num) {
        if (num < 10) {
            return true;
        }
        String s = String.valueOf(num);
        if (s.contains("0")) {
            return false;
        }
        int temp = num;
        while (temp > 0) {
            if (num % (temp % 10) != 0) {
                return false;
            }
            temp /= 10;
        }
        return true;
    }

然后Eclipse运行的时候,即使是最高的10^6级别的数据,时间也很快完成了,因此满心欢喜贴上代码,还在纳闷怎么这么少人答出,结果…
(您好,1-1000000,您的空间复杂度太高,可能的原因是创造了一个不需要的二维数组)
ミ゚Д゚彡

第二回合:空间复杂度太高?什么意思?没用什么额外空间啊??难道是动态规划,可以由前一个数递推下一个数?不可能啊,没理由递推啊。而且时间复杂度肯定是很低的,1-1000000所花时间也极少,空间复杂度怎么降低啊?
以上是我的OS,然后连续几天都纠结于,比如说数字含有7,应该怎么处理,含有5,怎么处理,含有3,6,9等等等等,然而并不能通过,空间复杂度始终没有长进

第三回合:1-1000000以内的数字中,其实符合条件的只有几千个,我将这些数都输出了,然后发现了一些特征:
相邻两个数自己的差值,不会超过两个数的最大位的数
也就是说两个数是9XXXXX和9YYYYY,这两个数都符合条件,那么这两个数肯定差值在9以上,反过来想,9XXXXX可以被9整除,那么下一个可能满足条件的就是这个数+9,且当数不超过1000000时,都可以以9为跨度,而不是以之前的1为跨度!
也就是说:以X为开头的数从第一个符合的数+X,会比原来少验很多肯定不符合要求的数字,如果以平均值5计算,100W以内数字可能只会验证大约1/5的数字,也就是20W左右,空间复杂度应该就够了
还有一点小优化:当当前数含有0的时候,比如说XX0XXX,那么下一个可能满足条件的数只能是XX1111

综上: 代码如下:

import java.util.ArrayList;
import java.util.List;

public class Solution {
    static String OneS = "1111111111111111";
    static String ZeroS = "0000000000000000";

    public static List<Integer> digitDivideNums(int lower, int upper) {
        List<Integer> ret = new ArrayList<Integer>();

        for (int i = lower; i <= upper;) {
            String s = String.valueOf(i);
            if (s.indexOf("0") == -1) {
                if (isDigitDivideNum(i)) {
                    ret.add(i);
                    if (i < 10 || Integer.valueOf(s.substring(0, 1)) == 1) {
                        i++;
                    } else {
                        int minUpper = calculate(s, upper);
                        //x是每次的跨度,minUpper是当前上限
                        int x = Integer.valueOf(s.substring(0, 1));
                        for (int j = i + x; j <= minUpper && j > 0; j += x) {
                            if (isDigitDivideNum(j)) {
                                ret.add(j);
                            }
                        }
                        i = minUpper;
                        if (minUpper == upper) {
                            break;
                        }
                    }
                } else {
                    i++;
                    if (i < 0) {
                        break;
                    }
                }
            } else {
                int index = s.indexOf("0");
                s = s.substring(0, index)
                        + OneS.substring(0, s.length() - index);
                i = Integer.valueOf(s);
            }
        }
        return ret;
    }

    //举个栗子,当前数为21112,则到30000为止为上限
    //部分代码奇怪是因为数字本身可能超过int的上限
    private static int calculate(String s, int upper) {
        int x = Integer.valueOf(s.substring(0, 1));
        long longTempUpper = Long.valueOf(x + 1
                + ZeroS.substring(0, s.length() - 1));
        //我们假定的上限超过了int上限,则上限改为int上限
        int tempUpper = longTempUpper > Integer.MAX_VALUE ? Integer.MAX_VALUE
                : (int) longTempUpper;
        return upper > tempUpper ? tempUpper : upper;
    }

    private static boolean isDigitDivideNum(int num) {
        if (num < 10) {
            return true;
        }
        String s = String.valueOf(num);
        if (s.contains("0")) {
            return false;
        }
        int temp = num;
        while (temp > 0) {
            if (num % (temp % 10) != 0) {
                return false;
            }
            temp /= 10;
        }
        return true;
    }
}

上述代码中有一些比较奇怪的代码片段,主要是因为测试用例里面到了int的最大值,也就是2^32 - 1, 超过边界直接变负数,负数还是符合我刚开始的限定条件的,因此后来我又加上了必须大于0的约束,long那部分是因为int最大值是2XXXXXX的形式,但是3000000000超过了int的上界,因此这部分需要把上界改为Integer.MaX_VALUE,且为了不影响其他代码,这里先用long将300000000存下来.

然后终于通过了,空间复杂度没有超过,喜极而泣٩(๑❛ᴗ❛๑)۶

谢谢您的阅读,希望对您有所帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值