数位DP模版
详细见灵神视频和文字版题解
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
代码模版(python)
#数位dp
#模版一般四个参数:(i,mask,isLimit,isPreNum)
#i表示填到i位,mask表示之前已经存储的条件的集合,可以不止一个参数,isLimit表示是否超出上限,isPreNum表示是否有前导数,这位有时可以忽略
def find(h:int)->int:
@cache#记忆化搜索
def f(i:int,mask:int,isLimit:bool,isPreNum:bool)->int:
s = str(h)
n = len(s)
if i == n:#递归边界
return int(isPreNum) #and check(mask)判断mask是否合法
res = 0
if not isPreNum:#isPreNum ==False 表示存在前导0,说明这一位不取的话也算前导0
res = f(i+1,mask,False,False)
up = int(s[i]) if isLimit else 9 #判断是否受上一个数位限制
low = 1-int(isPreNum) #判断是否存在前导0
for d in range(low,up+1):#循环遍历目前i的数码
#mask = .....改变mask的状态
res+=f(i+1,mask,isLimit and d==up,True)
return res
return f(0,0,True,False)#一般第一个数位都受限,有isLimit =True
return find(high)-find(low-1)#一般是判断范围[low,high]有多少个合法数可以这么写
相关题目练习
【leetcode】2827.范围中美丽整数的数目
题目描述
数据集范围
思路
判断出来这题是用数位dp之后,套模版和考虑递归边界的条件即可。
题目有两个限制条件:1、偶数数位数目与奇数数位数目相同;2、这个整数能被k整除。
针对第一个限制条件,我们只需使用mask记录偶数数位数目和奇数数位数目的差,最后在递归边界判断mask是否为0即可。
而对于第二个限制条件,记录每一个数字的能否被k整除显然是不科学的,因为这意味着有[low,high]个状态,即,因此我们用状态机的思想考虑能否进行状态转移而减少状态数目。考虑到对于任一两位数,例如
,有
,那么
对于k的余数可以通过
和
两个数位对于
的余数得到,那么进行数位dp的时候,第
位的余数可以通过第
位的余数和第
位的数位对于
的余数获得。因此我们只需要记录
个状态,其中
。
代码实现
class Solution:
def numberOfBeautifulIntegers(self, low: int, high: int, k: int) -> int:
#数位dp
#模版一般四个参数:(i,mask,isLimit,isPreNum)
#i表示填到i位,mask表示递归条件,isLimit表示是否超出上限,isPreNum表示是否有前导数
def find(h:int)->int:
@cache#记忆化搜索
def f(i:int,mask:int,isLimit:bool,isPreNum:bool,sub:int)->int:
s = str(h)
n = len(s)
if i == n:
return int(isPreNum) and mask == 0 and sub ==0
res = 0
if not isPreNum:
res = f(i+1,0,False,False,0)
up = int(s[i]) if isLimit else 9
low = 1-int(isPreNum)
for d in range(low,up+1):
tmp = 1 if d%2==1 else -1
res+=f(i+1,mask+tmp,isLimit and d==up,True,(sub*10+d)%k)
return res
return f(0,0,True,False,0)
return find(high)-find(low-1)
复杂度分析
- 时间复杂度:
,其中
为
的十进制长度即
,
为每个数码的状态数,记忆化搜索的时间复杂度为:状态总数
计算每一个状态的时间。状态总数为
,计算每一个状态的时间为
,因此该算法时间复杂度为
。
- 空间复杂度:
,即为搜索的状态总数。