当前位置: 首页 > news >正文

LeetCode LeetCode 2801. 统计范围内的步进数字数目 C++实现

这道题是经典的数位 DP(Digit DP)问题。我们可以直接沿用之前 Java 实现的记忆化搜索思路,将其转化为 C++ 代码。

核心思路
1. 前缀和思想:将求解闭区间 [low, high] 转化为 count(high) - count(low - 1)。
2. 记忆化搜索(DFS):
* 使用 dfs(pos, prevDigit, isLimit, isNum) 从高位向低位枚举。
* pos:当前遍历到的数位下标。
* prevDigit:上一位填入的数字(用于判断相邻数位差的绝对值是否为 1)。
* isLimit:当前位是否受上界字符串的限制。
* isNum:前面是否已经填入了非零的有效数字(用于处理前导 0)。
3. 字符串减一操作:由于 low 和 high 的长度可达 100,必须使用字符串来模拟大数减 1 的操作,以避免整数溢出。

C++ 代码实现

#include <vector>
#include <string>
#include <cstring>
#include <cmath>

using namespace std;

class Solution {
private:
static const int MOD = 1000000007;
string s;
// 记忆化数组:[位置][上一位数字(0-9)][是否受限制][前面是否已填数字]
// 上一位数字用 10 表示初始状态(前面没有数字)
int memo[105][11][2][2];

// 字符串模拟数字减 1(处理 low - 1,避免大数溢出)
string subtractOne(string str) {
int i = str.length() - 1;
// 从最后一位开始借位
while (i >= 0 && str[i] == '0') {
str[i] = '9';
i--;
}
// 如果还没减完(比如 1000 -> 0999)
if (i >= 0) {
str[i]--;
}
// 去掉前导 0
if (str[0] == '0' && str.length() > 1) {
return str.substr(1);
}
return str;
}

// 计算从 0 到字符串 s 代表的数字之间有多少个步进数字
long long dfs(int pos, int prevDigit, bool isLimit, bool isNum) {
// 递归终止条件:已经填完所有位置
if (pos == s.length()) {
// 如果前面填了有效数字,说明找到了一个合法的步进数字
return isNum ? 1 : 0;
}

// 如果该状态已经计算过,直接返回
if (memo[pos][prevDigit][isLimit][isNum] != -1) {
return memo[pos][prevDigit][isLimit][isNum];
}

long long res = 0;
// 确定当前位能填入的最大数字
int up = isLimit ? s[pos] - '0' : 9;

// 1. 处理前导 0 的情况:如果前面还没填数字,当前位可以继续填 0(跳过)
if (!isNum) {
res = (res + dfs(pos + 1, 10, false, false)) % MOD;
}

// 2. 枚举当前位填入的数字 d
// 起始数字:如果前面没填数字(!isNum),从1开始;否则从0开始
int start = isNum ? 0 : 1;
for (int d = start; d <= up; d++) {
// 如果前面已经填了数字,需要满足步进条件:相邻数字差的绝对值为 1
if (isNum && abs(d - prevDigit) != 1) {
continue;
}
// 递归进入下一位
res = (res + dfs(pos + 1, d, isLimit && d == up, true)) % MOD;
}

// 记录并返回结果
memo[pos][prevDigit][isLimit][isNum] = res;
return res;
}

long long count(string str) {
s = str;
// 初始化记忆化数组为 -1
memset(memo, -1, sizeof(memo));
return dfs(0, 10, true, false);
}

public:
int countSteppingNumbers(string low, string high) {
// 计算 [0, high] 的步进数字个数
long long countHigh = count(high);
// 计算 [0, low-1] 的步进数字个数
long long countLow = count(subtractOne(low));

// 结果为两者之差,注意取模防止负数
return (int)((countHigh - countLow + MOD) % MOD);
}
};

复杂度分析
* 时间复杂度:O(log N),其中 N 是 high 的数值。数位 DP 的状态数取决于数字的位数(最多 100 位)、上一位数字(0-9,共11种状态)、是否受限(2种)和是否已填数字(2种)。总状态数非常少,每次转移最多枚举 10 个数字,因此效率极高。
* 空间复杂度:O(log N),主要用于存储记忆化搜索的 memo 数组以及递归调用的栈空间。

http://www.rkmt.cn/news/1417799.html

相关文章:

  • CloudCanal 免费社区版:全自研数据迁移同步工具,功能对标大厂,亮点特性与优化修复齐上阵!
  • 5 款中老年时光相册工具实测,轻松留存人生回忆
  • 浏览器内秒级构建容器镜像,开发者工作流或迎变革
  • 2026现阶段杭州万能话筒优质厂家推荐几家:市场主流品牌选购攻略 - 2026年企业资讯
  • 2026中山工厂搬家公司推荐:实测5家靠谱不踩雷 - 从来都是英雄出少年
  • Qwen模型 LeetCode 2809. 使数组和小于等于 x 的最少时间 Java实现
  • Inno Setup 6.7.3 版本发布:改进 Wine RichEdit 方法、修复安全问题及函数编译问题
  • 基于PIR传感器与分立元件的智能花园驱鸟器DIY全解析
  • 2026 中山搬工厂公司实测盘点与避坑指南 - 从来都是英雄出少年
  • Cursor 3.3 终极技能解释:12个斜杠命令解锁AI编程
  • 太阴间了!程序员要加班到晚 10 点,但有人想方设法不让程序员“偷用公司空调”
  • Keil μVision4项目实战:手把手教你用T5L迪文屏给51单片机加个“漂亮脸蛋”
  • 【紧急更新】2024Q3最新版:ChatGPT汇报材料优化SOP(含中办公文格式API适配参数+敏感词动态过滤表)
  • 通达信缠论插件终极指南:3步实现自动化笔段中枢识别
  • 供水管网及泵站远程监控运维管理系统方案
  • 全球首例实战!伊朗APT Nimbus Manticore用AI打造MiniFast后门,深度解析AI驱动的网络战新形态
  • 3分钟诊断Windows热键冲突:Hotkey Detective帮你找回失效的快捷键
  • 2026年四川集装箱厂家TOP5排行:成都集装箱厂家、景区移动厕所、海运箱改造、环保公厕生产厂家、移动厕所出租选择指南 - 优质品牌商家
  • Windows 11/10 资源管理器卡死别慌!这3种重启explorer.exe的方法总有一个能救急
  • kubernetes的基于Operator实现Redis主从复制
  • DDrawCompat终极指南:让Windows经典游戏在现代系统上完美运行的免费兼容性方案
  • 使用 Taotoken CLI 工具一键配置多开发环境下的模型调用参数
  • 构建去中心化AI推理任务匹配系统:架构、挑战与实现
  • 在旧笔记本上复活Gentoo:超轻量级安装与i3wm平铺窗口管理器配置全流程
  • 基于HMC5883L与Arduino的电子指南针:从磁场感知到动态指针显示
  • 2026年近期西南地区餐椅采购指南:聚焦康定直销工厂联系方式与选型策略 - 2026年企业资讯
  • 保姆级教程!手把手教你安装 OpenClaw,小白也能一次成功
  • 【数据分析】分数阶混沌系统的混沌附matlab代码
  • 2026年5月北京二手房装修公司推荐:TOP5排名旧房翻新评测专业价格 - 品牌推荐
  • AI新范式:Agent的核心逻辑与四大模块深度解析!