`算法题解` `LeetCode` 2376. 统计特殊整数

题目链接

题解

问在[1, x]之间的(所有数字) 的某类性质, 这是数位DP的问题;

按照数位DP的规定, Prefix( r)表示: 在[0 - r]之间的所有数字
所以, 将其转换为: Prefix( x) - Prefix( 1 - 1),

数位DP会将[0 - r]的所有数字, 划分为3大类: 以r = 2345
(1, 第一类: 非对齐数字, 为所有< 1000的数, 即[0 - 999]这些)
(2, 第二类: 对齐数字, 为所有[1000, 2344]这些数, 可以发现, 这些数字的长度是相同的)
(3, 第三类: r = 2345)

一般, 优先考虑, (第二类数字) 是否有解; … 因为, 有时第一类数字的问题, 可以用与 (第二类数字) 相同的方法来解决

具体的, 第二类数字, 还会继续划分成:
([1000, 1999])
([2000, 2099] [2100, 2199] [2200, 2299])
([2300, 2309] [2310, 2319] [2320, 2329] [2330, 2339])
([2340, 2340] [2341, 2341] [2342, 2342] [2343, 2343] [2344, 2345])
… 具体规则可以回顾(数位DP)知识.

我们称, 上面这13个区间, 我们对(第二类数字)的处理过程, 就是 对这13个区间的处理; 即, 一个区间, 是我们最小的处理单元;
这每个区间, 他们都是可以表示为: abcd, 且某个前缀是固定的 后缀是0-9的任意
比如, 对于区间[2100, 2199], 他为 (固定前缀为21, 后缀任意), 所有区间都是这样的形式

因此, Aligned( vector< int> pre, int k)函数, 就是针对每个区间, 求解该区间的答案;
pre为固定前缀(没有前导零), k为后缀长度. 该区间有10^k个数; [2100, 2199]对应为Aligned( {2, 1}, 2)
那么, 比如对于[abc] [000 - 999]区间, 他的答案是多少呢?
(1, 如果abc是非法的 即, 存在两位相同, 则, 整个区间全为非法的; ans为0)
(2, 否则, abc互不相同, 则后缀有: [10-3种选择, 10-4种选择, 10-5种选择], 即ans = 7 * 6 * 5, 因为所有位都不同, 就类似于(排列问题).

至此, (第二类数字) 我们已解决;

看第一类数字: [0, 999], 他一般有两种处理方法:
(1, 尝试用Aligned (即第二类数字的处理方法)来解决, 通过将(第一类数字) 添加前导零, 使得 (第一类数字 与 第二类数字 ) 具有相同长度
… 即, 将[0, 999] 变成 [0000, 0999], 即Aligned( {0}, 3)的答案 是否等于 [0, 999]的答案呢?
… 答案是否定的! 0001Aligned里 会按照非法的来处理, 而0001他在[0, 999]里对应为1 他是合法的!!!
… 因此, 不可以添加前导零;
(2, 拆分集合; 将[0, 999]继续划分为: ([0, 0] [1, 9] [10, 99], [100, 999])
… 这样划分的依据在于: (每个区间, 都是等长的)
… 每个区间的长度为: 1, 9, 90, 900, ...
… 除了第一个区间[0, 0], 其他区间 都是: [1 后面全是0, 9 后面全是9]的形式;
… 对于每个区间, 继续进行划分: 比如[100, 999], 划分为: [100, 199] [200, 299] [300, 399] …, [900, 999] (共9个)
… 这样做的好处是: 对于每个小区间[200, 299], 他符合Aligned的形式, 直接调用Aligned( {2}, 2)即可
… … 而这里, 其实对于[1000, 9999], 也可以自己直接算: [9种, 9种, 8, 7, ..], ans = 9 * 9 * 8 * 7 ... (注意, 第一位是[1-9], 其他位都是[0-9])

更新

题目要求的 (特殊数 所有位不同), 那么, 数位DP 是选择计算(特殊数) 还是计算(非特殊数)呢?
… 这要看 [a b c] [000 - 999], 谁能在线性时间 处理这个集合的答案;
… 这里的(非特殊数), 并不是连续的, 比如: 12341是(非特殊数), 它是跳跃的; 这不是DP能处理的
… 而(特殊数), 每个位 都不同, 联系到(排列数)

注意点

  • 这是一个非DP的数位DP, 不需要Build_dp记忆化; 直接暴力即可

代码

bool Is_valid( 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值