【题型总结】数位dp(一)

简介: 【题型总结】数位dp(一)

数位动态规划

数位DP介绍

  • 可以解决的问题
    对于「数位 DP」题,都存在「询问[a, b](a 和 b 均为正整数,且 a符合特定条件的数值个数为多少」的一般形式,通常我们需要实现一个查询 [0,x] 有多少合法数值的函数 f(int x),然后应用「容斥原理」求解出 [a,b] 的个数:f(b)−f(a−1)
  • 这个区间可能很大,暴力代码一般会超时,此时就可以使用数位DP解决该类问题。
  • 由于数位是按位dp,数的大小对复杂度的影响很小,时间复杂度为状态个数 * 单个状态的转移次数
  • 模板题

试计算在区间 1到 n的所有整数中,数字 x(0≤x≤9)共出现了多少次?例如,在 1 到 11 中,即在 1,2,3,4,5,6,7,8,9,10,11 中,数字 1出现了 4次。

模板

image.png

作者:灵茶山艾府

链接:https://leetcode.cn/problems/number-of-digit-one/solutions/1750339/by-endlesscheng-h9ua/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关题目

1.数字1的个数【LC233】

2022/11/25

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

image.png

实现

  • 首先将n转化为字符串数组s,存储高位至低位的数值大小,会影响isLimit
  • 然后使用dp数组记录在不受到约束时,第i位枚举值固定时的合法方案数,即从左往右第i位之后所有枚举数中1的个数【不包括第i位】
  • 然后通过for循环,枚举每一位其所有可能的值
  • 举例说明:计算所有小于等于 n 的非负整数中数字 1 出现的个数。

如果当前的枚举数不受限,并且状态dp[i,cnt1]之前已经计算过,那么此时第i位枚举值固定时的合法方案数不需要重新计算,直接返回dp[i,cnt1]即可

class Solution {
    char s[];
    int dp[][];
    public int countDigitOne(int n) {
        s = Integer.toString(n).toCharArray();
        int m = s.length;
        dp = new int[m][m];
        for (int i = 0; i < m; i++) Arrays.fill(dp[i], -1);
        return f(0, 0, true);// 第i位受isLimit约束
    }
    int f(int i, int cnt1, boolean isLimit) {
        if (i == s.length) return cnt1;
        if (!isLimit && dp[i][cnt1] >= 0) return dp[i][cnt1];
        int res = 0; // 记录从左往右第i+1位之后所有枚举数中1的个数
        for (int d = 0, up = isLimit ? s[i] - '0' : 9; d <= up; ++d)
            // d代表枚举要填入的数字 up代表上限值 不受限则为9 受限则为第i位的值
            // 以下代码运用到了回溯
            // 具体含义为:寻找从左往右第i+1为以及之后数位中1的个数,因此需要更新cnt1以及isLimit
            // cnt1含义为前面i位填充1的个数,因此如果当前位填入的数字为i,那么cnt1 + 1,否则加0
            // isLimit含义为该位填充数字是否受限,取决于前一次填充是否受限,以及当前填充是否达到up
            res += f(i + 1, cnt1 + (d == 1 ? 1 : 0), isLimit && d == up);
        if (!isLimit) dp[i][cnt1] = res;// dp数组的含义为在不受到约束时第i位的合法方案数,即从左往右第i位之后所有枚举数中1的个数【不包括第i位】
        return res;
    }
}

复杂度

  • 时间复杂度:O(logn),$\lceil log_{10}n \rceil+1$为数字的位数,时间复杂度 = 状态个数 * 单个状态的转移次数,状态个数就是 dp 数组的长度,即O(logn),而单个状态的转移次数 = O(2) = O(1),所以时间复杂度为O(logn)
  • 空间复杂度:O(logn)

2.不含连续1的非负整数【LC600】

Given a positive integer n, return the number of the integers in the range [0, n] whose binary representations do not contain consecutive ones.

2022/11/25

LC233的思路相同,区别在于把数使用二进制表示

  • 思路:数位dp
    首先将n转换成二进制int数组binary,定义f(i,pre,isLimit,isNum)表示构造从左往右第i位及其之后数位的合法方案数,即不存在连续1的个数,其余参数的含义为:

image.png

实现

  • 首先将n转化为二进制形式,并使用int数组binary存储
  • 然后使用dp[i][j]数组代表在不受到约束时,第i位枚举值固定为j时的,合法方案数,即从左往右第i位之后所有枚举数中不含1的个数【不包括第i位】
  • 然后对于每一位枚举其所有可能的值,由于枚举值只可能为0或者1,因此可以不使用for循环,直接判定
  • 如果不受限并且前一位不为1并且最大值为0,那么第i位可以填充1
  • 任何情况均可以填充0
  • 代码
class Solution {
    int [] binary;
    int[][] dp;
    public int findIntegers(int n) {
        int m = 32;        
        binary = new int[m];// 转化为二进制数组
        for (int i = 0; i < m; i++){
            binary[i] = (n >> (31-i)) & 1;
        }        
        dp = new int[m][2];
        for (int i = 0; i < m; i++){
            Arrays.fill(dp[i],-1);
        }
        int i = 0;
        while (i < m && binary[i] == 0){// 找到第一个不为0的二进制
            i++;
        }
        return f(i,0,true);
    }
    public int f(int i, int pre, boolean isLimit){
        if (i == binary.length){
            return 1;
        }
        if (!isLimit && dp[i][pre] >= 0){// 不受限 并以及计算过
            return dp[i][pre];
        }
        int res = 0;// 记录第i位之后的枚举可能性
        int up = isLimit ? binary[i] : 1;// 
        res += f(i + 1, 0, isLimit && up == 0);// 第i+1位填0 
        if (pre != 1 && up == 1){
            res += f(i + 1, 1, isLimit);// 第i位填1
        }               
        if (!isLimit){
            dp[i][pre] = res;
        }
        return res;
    }    
}

image.png

【题型总结】数位dp(二):https://developer.aliyun.com/article/1405899?spm=a2c6h.13148508.setting.14.45fd4f0eFyVSCn

目录
相关文章
|
iOS开发
iOS - QRCode 二维码
1、QRCode 在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing。iOS7 之后可以利用系统原生 API 生成二维码, iOS8 之后可以生成条形码, 系统默认生成的颜色是黑色。
3283 0
|
14天前
|
Java 编译器 Windows
JDK 21安装教程:Windows系统一步到位(附常见问题解决)
本文详解Windows下JDK 21安装全流程:从下载、管理员运行、路径选择到环境变量验证,并汇总权限不足、命令未识别、版本冲突等常见问题及解决方案,助新手快速完成Java开发环境搭建。
460 0
|
8月前
|
人工智能 运维 API
高级RAG优化手册:3招解决检索不准和查询模糊
本文深入解析RAG(检索增强生成)技术的核心优化方法,涵盖背景、架构与实践。RAG通过整合外部知识库,弥补大语言模型在实时性、准确性和专业性上的不足,广泛应用于企业场景。文章系统讲解RAG如何解决知识静态、生成幻觉与专业深度不足等问题,并剖析其离线索引与在线生成的闭环流程。此外,还介绍了高级优化策略,如查询重写、混合检索与结果重排序,助力突破RAG应用瓶颈。
1807 1
|
机器学习/深度学习 人工智能 自然语言处理
Qwen3:小而强,思深,行速
Qwen3(千问3)于北京时间4月29日凌晨发布,是Qwen系列大型语言模型的最新成员,具备全系列、开源最强、混合推理等特性。它包括两款MoE模型(Qwen3-235B-A22B和Qwen3-30B-A3B)及六个Dense模型,支持119种语言。Qwen3在代码、数学和通用能力测试中超越行业顶尖模型,如DeepSeek-R1和Grok-3。其旗舰版Qwen3-235B-A22B仅需4张H20即可本地部署,成本为DeepSeek-R1的35%。此外,Qwen3原生支持思考模式与非思考模式切换,降低复杂任务门槛,并支持MCP协议优化Agent架构。
9058 2
|
搜索推荐 数据挖掘 双11
淘宝运营进阶秘籍:从业余到专业
淘宝运营涉及市场分析、产品定位、店铺装修、营销推广、客户服务、数据分析等多环节。要脱颖而出,需掌握进阶秘籍。本文从精准定位、店铺装修、定价策略、流量获取、客户服务、数据分析及跨平台合作七大方面深入探讨,助商家实现从平凡到卓越的蜕变。通过目标受众分析、优化店铺形象、合理定价、多种促销手段、提升客户体验、利用数据反馈调整策略以及拓展销售渠道,商家可逐步提升运营能力,在竞争激烈的电商环境中取得成功。
1883 4
Manacher(马拉车)算法详解
该文章详细解释了Manacher算法,这是一种高效找出给定字符串最长回文子串的算法,通过在字符串中插入特殊字符构建新的字符串,并利用中心扩展策略来找出最长回文序列,时间复杂度为O(N),空间复杂度为O(N)。
|
负载均衡 算法 前端开发
Keepalived + Nginx 实现高可用 Web 负载均衡
Keepalived + Nginx 实现高可用 Web 负载均衡
Keepalived + Nginx 实现高可用 Web 负载均衡
|
Web App开发 存储 前端开发
❤️使用 HTML、CSS 和 JS 创建在线音乐播放器(含免费完整源码)❤️
❤️使用 HTML、CSS 和 JS 创建在线音乐播放器(含免费完整源码)❤️
1435 0
|
弹性计算 Ubuntu Unix
安装 NextCloud 网盘程序| 学习笔记
快速学习安装 NextCloud 网盘程序。
安装 NextCloud 网盘程序| 学习笔记
|
网络安全 Docker 容器
docker启动出现Error response from daemon: Cannot restart container的报错
docker启动出现Error response from daemon: Cannot restart container的报错
docker启动出现Error response from daemon: Cannot restart container的报错