题目分析
看到本题,我的第一反应是一个数学问题,首先模拟一下解题过程。首先数字不能出现3、4、7,因为一旦出现,一定是不能被旋转的。然后再分析2、5、6、9,这四个数字是必须至少出现一次的,如果一次都不出现,那么剩下的数字是0、1、8,无论如何旋转都是原数字。
递归+数学+模拟
所以本题的思想就是,在diff = [2, 5, 6, 9]中至少选择一个,然后其他数字均在same = [0, 1, 8]中。我们从最高位X开始
- 最高位在diff中的情况
如果X等于2、5、6、9时,因为最高位已经出现了一次diff数组的元素,因此剩下的位数可以任意选择。要统计去掉最高位时,数字仅由all集合组成的个数,比如654,就需要统计54中数字仅由all集合组成的个数。因为其他数字的选择不受限制,创建一个名为noLimit的函数,表示可以取all中的所有元素。
- 最高位在same中的情况
如果X等于0、1、8时,因为最高位没有出现diff数组的元素,因此剩下的位数是受限制的。要统计去掉最高位时,出现一次diff数组的数字,那就是本题自身,因此递归调用本方法即可。如果854,就需要统计54中至少出现一次diff的元素。
- 考虑最高位小于X,并且出现在diff中的情况
那么X以下的2、5、6、9,低位是可以表示从0到全9的。比如854的最高位是8,那么对于2、5、6来说,可以统计99中数字仅由all集合组成的个数。
- 考虑最高位小于X,并且出现在diff中的情况
那么X以下的1、2、8,低位是可以表示从0到全9的。比如854的最高位是8,那么对于1、2来说,可以统计99中至少出现一次diff的元素。
记n的最高位数字是x,除去最高位的数字为y,除了最高位其他全9的数字为z。记本题方法名为rotatedDigits,不受限的方法名为noLimit。小于X的元素,在diff中的个数为p,在same中的个数为q
$$ rotatedDigits(n) = p x noLimit(z) + q x rotatedDigits(z) + diff.contains(x) ? noLimit(y) : rotatedDigits(y) $$
算法的**时间复杂度为O(log(n)),空间复杂度为O(log(n))**。
1 | class Solution { |
数位DP
上述方法是一个很好的方法,其中包含着一些求解数学问题的技巧,本题还可以使用数位DP来求解。
根据递归方法中的递推公式,其实可以看出,本题是可以使用DP来做的,只不过DP比较复杂,无法通过一维来表示。这里给出一种通用的DP算法,使用三维DP(本题是可以优化到二维的)
因为数字可以在集合same中、也可以在集合diff中,因此需要一个维度来表示,0表示没有出现在diff中,1表示已经有元素出现在diff中了。
因为数字的最大值会限制后面的取值,如果当前遍历的数字等于最大值,那么后面的元素也是受限的,比如854,遍历第一位到8时,后面只能考虑到54,如果遍历到7,则后面可以考虑到99。因此也需要一个维度来表示,0表示不受限,1表示受限。
这里有两个位运算的逻辑,如果containDiff已经是1了,那么遍历后面的元素时,无论是否为0,都是1。因为已经出现过一次了,哪怕后面不出现,也表示出现过一次了。
如果limit已经是0了,那么遍历后面的元素时,无论是否等于最大值,都是0。因为最高位已经不被限制了,那么低位被限制也不会是最大值。如854、如果最高位取7时,次高位取最大9,也不会影响最后一位取值为9。
算法的**时间复杂度为O(log(n)),空间复杂度为O(n)**。
1 | class Solution { |
刷题总结
数位DP也是一种比较难想到的动态规划,有时间给小伙伴们多介绍几个题目。