LeetCode高频题:int数字x被拆为p1,p2,p3,输入N必须满足N=p1+p2/p3,p2/p3整除,N有多少种分数形式

LeetCode高频题:int数字x被拆为p1,p2,p3,输入N必须满足N=p1+p2/p3,p2/p3整除,N有多少种分数形式?

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述
本题基础知识:
【1】LeetCode高频题:请你打印int数字x的所有全排列,x只包含且必须包含1-9的每一个数字


题目

LeetCode高频题:int数字x被拆为p1,p2,p3,输入N必须满足N=p1+p2/p3,p2/p3整除,N有多少种分数形式
实际上这么理解:

int数字x被拆为p1,p2,p3,必须满足N=ans=p1+p2/p3,p2/p3整除,使得ans成立的x有多少个?

其中,x必须包含1–9,且只包含1-9中的每一个数字
1-9不重复出现

即给你N,请问你能从x(包含1-9的任意排列数字中)拆为p1+p2/p3,且p2/p3整除;
使得N=ans=p1+p2/p3成立的x有多少个???
N<10的8次方


一、审题

示例:比如
N=ans=100
那从x=123456789的所有排列组合中,拆出p1,p2,p3,使得N=ans=p1+p2/p3,有多少个数字呢?
在这里插入图片描述
x=82 3546 714

p1=82
p2=3546
p3=714

p2/p3=18

ans=100=p1+p2/p3
算一种,然后你继续拆别的x【必须包含1–9的排列】的可能性?
对于100
能找到的x有11个


二、解题

也就是说,包含1–9的x,x可能是任意排列,比如
x=123456789
可以把从index=8–2位随意拆,左边是p1
右边是rest部分
为啥index只能是8–2位呢???
在这里插入图片描述
你最起码要把1位留给p2,把0位留给p3
否则人家p2/p3如何整除???

OK,p1拆出来了
那么除号又怎么拆呢???
整除,要求p2的位数>=p3的位数
在这里插入图片描述
要是p2数量不足p3,那你就是分数,不叫整除了……

其实,你注意,如果在x的index处拆左边为p1,实际上拆p2,p3的位置j应该是啥呢?至少<=index/2
因为你拆的位置高了p2就数量不足了
比如下面i=7位置左边就是p1,则p2被拆的位置一定是低于7/2=3位置的
这样2–0是p3
在这里插入图片描述
这才算是一个合理的p1,p2,p3

如果我们发现p2%p3=0【整除】且ans=p1+p2/p3,则我们可以认定x算一种成功的拆分方案!ans的计数count增加
下次同一个ans再拆别的x的方案,OK的话,我们可以把ans次数count++

因此,本题的思路,就是暴力枚举1-9所有的排列x,然后让x拆p1,p2,p3,判断每一种拆分情况是否满足条件ans=p1+p2/p3,是让ans的词频count++,否则不管

本题的思路:
(1)就是暴力枚举1-9所有的排列x,
(2)然后让x拆p1,p2,p3,判断每一种拆分情况是否满足条件ans=p1+p2/p3,是让ans的词频count++,否则不管

枚举所有的1–9的全排列,一共实际上就是9!=362880次交换操作,我们讲过
【1】LeetCode高频题:请你打印int数字x的所有全排列,x只包含且必须包含1-9的每一个数字

我们直接在【1】之上的代码中改本题的代码:

枚举所有x的代码

    //就把x的L=7位置和R=2位置的俩数字交换了
    public static final int[] arr = {1,10,100,1000,10000,100000,1000000,10000000,100000000};
    //准备好常量arri表示1后面跟i个0

    public static int swap(int x, int L, int R){
        //将x的L和R处数字交换
        //首先把L和R处数字提取出来,就可以利用加减法换L和R
        int numL = x / arr[L] % 10;
        int numR = x / arr[R] % 10;
        //L这里是x-([L]-[R])*10的L次方=a
        //R这里是a+([L]-[R])*10的R次方
        int a = x - (numL - numR) * arr[L];//完成L变R
        int b = (numL - numR) * arr[R];//完成R变L

        return a + b;//最终L和R调换,以后千万别把int转char[],这样很费时间的
    }

    //我定义递归函数**f(x,index)**
    //来到x的index位,先把x固定一个数字做排头(1--9都要尝试排头),剩下的x从index+1位置往后去继续递归
    public static void f(int x, int index){
        if (index == -1){
            System.out.println(x);//index来到-1处,说明8--0位置的排头决定好了
        }else {
            //来到index,让谁来做排头?index--N-1位置每个数字轮流做,不是数组,不需要恢复现场
            for (int j = index; j >= 0; j--) {//从高位L跟低位R=j处换
                int next = swap(x, index, j);//index和j换数字
                f(next, index - 1);//然后去index=1处玩剩下的数字——不需要恢复现场,x没有动过
                //待会f结束,x依然是x,然j轮流做排头就行了
            }
        }
    }

当index来到-1位置,说明x已经是一个枚举x情况

我们来拆分x
(1)拆p1,x从8–2,一共就枚举7次,将x拆为p1和rest
(2)内部,拆rest为拆为p2,p3,从index/2–1处,一共枚举这么多次,可能会枚举8/2,7/2,6/2,5/2,4/2,3/2,2/2
算最复杂的情况7*(4+3+3+2+2+1)=7*15=105次枚举

x有9!种情况

因此就操作9!*105=5810 2400次操作,也没有到10的8次方呢!
因此可以笔试AC的【因为java要求计算机4s没最高运算次数10的8次方】

这个枚举方法就可以暴力搞定所有的拆分情况,所有的x可能性

于是呢,我们整一个哈希表map,将ans=p1+p2/p3的拆分成功达标的ans的数量记录下来
因为不同的拆分可能得到同一个ans

当然,如何拆p1,p2,p3呢??无非就是一些除法,取模,仅此而已

你怎么把x中的p1捞出来
不就是让x/10的index次方吗?
在这里插入图片描述
同理,你怎么把rest捞出来呢???
不就是让x%10的index次方吗???

非常非常非常非常容易的计算

OK,p2,p3不就是拆rest吗???
j处拆,那p2=rest/10的j次方,p3=rest%10的j次方
在这里插入图片描述
好了,我们就是干这么几件事
(1)枚举1–9所有的全排列,一旦确定index=8—0所有位置,index=-1了,这时候的x就是一种排列数字
(2)拿着x,拆p1,p2,p3,p1的枚举可能是i=8–2所有位置,当p1确定了的情况下,p2从j=i/2位置–1位置
(3)如果p2%p3=0,把所有可能的ans=p1+p2/p3的情况达标的ans,用map记录ans的词频count++

手撕代码:

    //好了,我们就是干这么几件事:
    //LeetCode高频题:int数字x被拆为p1,p2,p3,必须满足x=p1+p2/p3,p2/p3整除,x有多少种划分方案
    public static HashMap<Integer, Integer> map = new HashMap<>();//全局
    public static void findOKNumber(int x, int index){
        if (index == -1){
            //index=-1了,这时候的x就是一种排列数字
            //(2)拿着x,拆p1,p2,p3,p1的枚举可能是i=8--2所有位置,当p1确定了的情况下,p2从j=i/2位置--1位置
            //如果p2%p3=0,把所有可能的ans=p1+p2/p3的情况达标的ans,用map记录ans的词频count++
//            System.out.println(x);//index来到-1处,说明8--0位置的排头决定好了
            for (int i = 8; i >= 2; i--) {//枚举p1的位置
                int p1 = x / arr[i];//arr就是10的i次方,1后面跟i个零
                int rest = x % arr[i];
                for (int j = (i >> 1); j >= 1; j--) {//枚举p2的位置
                    int p2 = rest / arr[j];//arr就是10的j次方,1后面跟j个零
                    int p3 = rest % arr[j];
                    if (p2 % p3 == 0){
                        //看看哪些ans=p1+p2/p3--x是很多很多很多的排列组合1--9必须包含,ans可能很多
                        int ans = p1 + p2/p3;
                        if (!map.containsKey(ans)) map.put(ans, 1);//初次见面1次
                        else map.put(ans, map.get(ans) + 1);//词频增加
                    }
                }
            }
        }else {//(1)枚举1--9所有的全排列,一旦确定index=8---0所有位置,
            //来到index,让谁来做排头?index--N-1位置每个数字轮流做,不是数组,不需要恢复现场
            for (int j = index; j >= 0; j--) {//从高位L跟低位R=j处换
                int next = swap(x, index, j);//index和j换数字
                findOKNumber(next, index - 1);//然后去index=1处玩剩下的数字——不需要恢复现场,x没有动过
                //待会f结束,x依然是x,然j轮流做排头就行了
            }
        }
    }
    //主函数
    public static int countNumberOk(int ans){

        findOKNumber(123456789, 8);//从x的8位置开始玩,map填好
        return map.getOrDefault(ans, 0);//统计结果返回
    }

代码就是上面【1】中f改编来的
你只需要把x=123456789所有排列试一遍
就能把map填好,然后获取map.get(ans)就是咱们要的可能出现的种数

测试:

    public static void test(){
        int ans = 100;
//        System.out.println(swap(x, 7, 2));
//        f(x, 8);//8--0这9个数字
        System.out.println(countNumberOk(ans));
    }

    public static void main(String[] args) {
        test();
    }

对于ans=100

11

如何,明白了吧

之所以暴力枚举x的9!次方次
还枚举p1的位置为7种可能,下面p2的位置又是i/2种
连成起来不过就是3810 2400次操作,也没有到10的8次方呢!完全可以通过题目设定的限制数据量小于10的8次方。


总结

提示:重要经验:

1)最重要的就是如何枚举int类型的数字x的全排列,中间涉及到很重要的交换函数,把int类型数字x的L和R位置交换
2)另外,如何把一个x拆分为p1,p2,p3,然后看看达标记录map,拆分的方法就是除法,取模,小问题。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值