每日打卡
题目描述
给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[ i] 来写的数字。例如,如果 digits = [ '1' , '3' , '5' ] ,我们可以写数字,如 '13' , '551' , 和 '1351315 '。
返回 可以生成的小于或等于给定整数 n 的正整数的个数 。
示例 1 :
输入:digits = [ "1" , "3" , "5" , "7" ] , n = 100
输出:20
解释:
可写出的 20 个数字是:
1 , 3 , 5 , 7 , 11 , 13 , 15 , 17 , 31 , 33 , 35 , 37 , 51 , 53 , 55 , 57 , 71 , 73 , 75 , 77.
示例 2 :
输入:digits = [ "1" , "4" , "9" ] , n = 1000000000
输出:29523
解释:
我们可以写 3 个一位数字,9 个两位数字,27 个三位数字,
81 个四位数字,243 个五位数字,729 个六位数字,
2187 个七位数字,6561 个八位数字和 19683 个九位数字。
总共,可以使用D 中的数字写出 29523 个整数。
示例 3 :
输入:digits = [ "7" ] , n = 8
输出:1
提示:
1 <= digits. length <= 9
digits[ i] . length == 1
digits[ i] 是从 '1' 到 '9' 的数
digits 中的所有值都 不同
digits 按 非递减顺序 排列
1 <= n <= 10 ^ 9
解题思路
这是一道动态规划之数型dp的题目,然后这道题目我们通过它的前身递归+记忆化搜索来解决。 接下来我们就来说说递归的参数怎么设计: isLimit 表示当前是否受到了 n 的约束。若为真,则第 i 位填入的数字至多为 s[i],否则至多为 9。例如 n=1314,如果前面填了 131,那么最后一位至多填 4;如果前面填的不是 131,那么最后一位至多填 9。如果在受到约束的情况下填了s[i],那么后续填入的数字仍会受到 n 的约束。 isNum 表示 i前面的数位是否填了数字。若为假,则当前位可以跳过(不填数字),或者要填入的数字至少为 1;若为真,则必须填数字,且要填入的数字从 0 开始。这样我们可以控制构造出的是一位数/两位数/三位数等等。对于本题而言,要填入的数字可直接从digits 中选择。
public int dfs ( int index, boolean isLimit, boolean hasNum) { }
解题代码
class Solution {
String [ ] digits;
int [ ] dp;
char [ ] str;
public int atMostNGivenDigitSet ( String [ ] digits, int n) {
this . digits= digits;
str= String . valueOf ( n) . toCharArray ( ) ;
dp= new int [ str. length] ;
Arrays . fill ( dp, - 1 ) ;
return dfs ( 0 , true , false ) ;
}
public int dfs ( int index, boolean isLimit, boolean hasNum) {
if ( index== str. length) {
return hasNum? 1 : 0 ;
}
if ( ! isLimit&& hasNum&& dp[ index] != - 1 ) {
return dp[ index] ;
}
int res= 0 ;
if ( ! hasNum) {
res= dfs ( index+ 1 , false , false ) ;
}
char up= isLimit? str[ index] : '9' ;
for ( String temp: digits) {
if ( temp. charAt ( 0 ) > up) break ;
res+= dfs ( index+ 1 , isLimit&& up== temp. charAt ( 0 ) , true ) ;
}
if ( ! isLimit&& hasNum) {
dp[ index] = res;
}
return res;
}
}
运行结果