力扣题解2376

news2024/11/15 19:40:13

大家好,欢迎来到无限大的频道。

今日继续给大家带来力扣题解。

题目描述(困难):

统计特殊整数

如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。

给你一个 正 整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。

​解题思路:

要计算区间 ([1, n]) 之间的特殊整数数量,可以按以下步骤进行:

  1. 定义特殊整数:

    • 一个特殊整数是指其每一个数位都是互不相同的,即没有重复的数字。

  2. 遍历和检查:

    • 遍历每一个整数从 1 到 n,检查每个整数是否为特殊整数。

    • 对于每个整数,可以将其分解为其各个数字,并检查这些数字是否有重复。

  3. 利用集合判断重复:

    • 可以通过一个数组或集合来记录某个数字是否已经出现。

  4. 计数特殊整数:

    • 如果当前数字是特殊整数,将计数器加一。遍历结束后,输出计数器的值。

参考代码如下:

bool is_special_integer(int num) {
    bool digit_seen[10] = {false}; // 标记每个数字是否出现过
    while (num > 0) {
        int digit = num % 10; // 取出当前的末尾数字
        if (digit_seen[digit]) { // 如果该数字已经出现过
            return false; // 不是特殊整数
        }
        digit_seen[digit] = true; // 标记该数字出现过
        num /= 10; // 去掉当前数字
    }
    return true; // 所有数字都互不相同
}
​
int countSpecialNumbers(int n) {
    int count = 0; // 特殊整数计数器
    for (int i = 1; i <= n; i++) {
        if (is_special_integer(i)) { // 判断是否为特殊整数
            count++; // 增加计数
        }
    }
    return count; // 返回特殊整数的数量
}

代码解析:

  1. is_special_integer 函数:

    • 输入一个整数 num 并检查其每位数字是否互不相同。使用一个布尔数组 digit_seen 来记录 0-9 这 10 个数字的出现情况。

  2. countSpecialNumbers 函数:

    • 遍历 1 到 n 的所有整数,调用 is_special_integer 函数来判断该数字是否是特殊的,并更新计数器。

但是此代码的时间复杂度过高,测试用例超过了限定时间,所以要进行优化。

解题思路:

  1. 理解特殊整数:

    • 特殊整数是在其每一位上没有重复的数字(例如,123、456是特殊整数,但 112、121、123 等不是)。

  2. 动态规划 + 记忆化搜索:

    • mask:用于表示当前已经使用的数字,采用位掩码。

    • prefixSmaller:表示当前构建的前缀数字是否小于目标数字的前缀。

    • nStr:将 ( n ) 转换成字符串形式,以便逐位处理各数字。

    • 使用动态规划和哈希表来存储已经计算过的状态,以避免重复计算,提升效率。

    • 状态定义:

  3. 递归函数 (DP):

    • 通过递归函数 dp 实现动态规划,当当前使用的数字数量等于 ( n ) 的位数时返回 1。

    • 对于每个数字,可以根据当前的状态,在允许的范围内选择下一个数字(既 [0,9] 中未被使用的数字)。

    • 通过哈希表 memo 存储计算过的 mask 和 prefixSmaller 的组合,以加速后续计算。

  4. 计算统计:

    • 首先计算位数小于 ( n ) 的所有特殊整数数量;

    • 然后计算与 ( n ) 有相同位数的特殊整数数量。

参考代码如下:

typedef struct {
    int key;
    int val;
    UT_hash_handle hh;
} HashItem; 
​
HashItem *hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}
​
bool hashAddItem(HashItem **obj, int key, int val) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
    pEntry->key = key;
    pEntry->val = val;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}
​
bool hashSetItem(HashItem **obj, int key, int val) {
    HashItem *pEntry = hashFindItem(obj, key);
    if (!pEntry) {
        hashAddItem(obj, key, val);
    } else {
        pEntry->val = val;
    }
    return true;
}
​
int hashGetItem(HashItem **obj, int key, int defaultVal) {
    HashItem *pEntry = hashFindItem(obj, key);
    if (!pEntry) {
        return defaultVal;
    }
    return pEntry->val;
}
​
void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);  
        free(curr);
    }
}
​
int dp(int mask, bool prefixSmaller, const char *nStr, HashItem **memo) {
    if (__builtin_popcount(mask) == strlen(nStr)) {
        return 1;
    }
    int key = mask * 2 + (prefixSmaller ? 1 : 0);
    if (!hashFindItem(memo, key)) {
        int res = 0;
        int lowerBound = mask == 0 ? 1 : 0;
        int upperBound = prefixSmaller ? 9 : nStr[__builtin_popcount(mask)] - '0';
        for (int i = lowerBound; i <= upperBound; i++) {
            if (((mask >> i) & 1) == 0) {
                res += dp(mask | (1 << i), prefixSmaller || i < upperBound, nStr, memo);
            }
        }
        hashAddItem(memo, key, res);
    }
    return hashGetItem(memo, key, 0);
}
​
int countSpecialNumbers(int n) {
    char nStr[64];
    sprintf(nStr, "%d", n);
    int res = 0;
    int prod = 9;
    int len = strlen(nStr);
    HashItem *memo = NULL;
    for (int i = 0; i < len - 1; i++) {
        res += prod;
        prod *= 9 - i;
    }
    res += dp(0, false, nStr, &memo);
    hashFree(&memo);
    return res;
}

代码分析:

1. 哈希表结构与操作
typedef struct {
    int key;
    int val;
    UT_hash_handle hh; // 哈希表的内部处理
} HashItem; 
  • 定义了一个结构体 HashItem,用于存储键值对(key 和 val),并使用 UT_hash_handle 宏,以便利用 uthash 提供的哈希表功能。

  • 哈希表相关操作如下:

HashItem *hashFindItem(HashItem **obj, int key);
bool hashAddItem(HashItem **obj, int key, int val);
bool hashSetItem(HashItem **obj, int key, int val);
int hashGetItem(HashItem **obj, int key, int defaultVal);
void hashFree(HashItem **obj);
  • hashFindItem 查找哈希表的项。

  • hashAddItem 添加项到哈希表,如果已存在则返回 false。

  • hashSetItem 设置某项的值,如果不存在,则插入新项。

  • hashGetItem 获取项的值,若不存在则返回默认值。

  • hashFree 释放哈希表的内存。

2. 动态规划函数 dp
int dp(int mask, bool prefixSmaller, const char *nStr, HashItem **memo) {
    // 检查当前使用的数字数量是否等于目标字符串的数量
    if (__builtin_popcount(mask) == strlen(nStr)) {
        return 1; // 如果是,返回 1,表示成功构造了一个有效的特殊整数
    }
    
    // 生成一个唯一的键以访问缓存
    int key = mask * 2 + (prefixSmaller ? 1 : 0);
    
    // 如果当前状态没有被计算过
    if (!hashFindItem(memo, key)) {
        int res = 0;
        int lowerBound = (mask == 0) ? 1 : 0; // 第一个数字不能是 0
        // 确定当前可以选择的数字上限
        int upperBound = prefixSmaller ? 9 : nStr[__builtin_popcount(mask)] - '0';
        
        // 尝试选择每一个数字
        for (int i = lowerBound; i <= upperBound; i++) {
            if (((mask >> i) & 1) == 0) { // 检查当前数字是否未被使用
                // 递归调用,加入当前选择并更新状态
                res += dp(mask | (1 << i), prefixSmaller || (i < upperBound), nStr, memo);
            }
        }
        hashAddItem(memo, key, res); // 缓存当前状态的结果
    }
    return hashGetItem(memo, key, 0); // 返回缓存的结果或 0
}
  • dp 函数中,使用 mask 来表示当前使用的数字,利用位运算避免重复。

  • 使用 prefixSmaller 判断当前构造的数字是否小于对应的 ( n ) 的前缀。

  • 计算当前状态下所能构造的特殊整数数量,并将其缓存到哈希表中。

3. 主函数 countSpecialNumbers
int countSpecialNumbers(int n) {
    char nStr[64];
    sprintf(nStr, "%d", n); // 将 n 转换为字符串形式
    int res = 0;
    int prod = 9; // 可能的数字引导初始值为 9
    int len = strlen(nStr); // 目标长度
    HashItem *memo = NULL; // 初始化缓存
    
    // 计算所有位数小于 n 的特殊整数的数量
    for (int i = 0; i < len - 1; i++) {
        res += prod; // 将当前的数量加入结果
        prod *= 9 - i; // 更新可能的选择数
    }
    
    res += dp(0, false, nStr, &memo); // 计算与 nStr 长度相同的特殊整数
    hashFree(&memo); // 释放内存
    return res; // 返回总结果
}
  • 主函数中,首先将 ( n ) 转为字符串以便逐位处理。

  • 计算所有位数小于 ( n ) 的特殊整数数量。

  • 通过 dp 函数计算与 ( n ) 有相同位数的特殊整数数量。

  • 最后释放内存并返回结果。

总结:

整段代码结合了动态规划与哈希表的缓存机制,优雅地解决了问题:

  • 通过位掩码有效记录已使用的数字。

  • 通过记忆化搜索加速计算,避免重复递归。

  • 提供了高效的方式来计算区间 ([1, n]) 中所有特殊整数的数量,可以适用于较大的数字范围。

这种方法的时间复杂度主要由位数 ( m ) 影响,且结合了组合的计算,通常在实际问题中能够高效运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2150511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python报错已解决】SyntaxError invalid syntax

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

锐尔15注册机 锐尔文档扫描影像处理系统15功能介绍

锐尔文档扫描影像处理系统是一款全中文操作界面的文件、档案扫描及影像优化处理软件&#xff0c;是目前国内档案数字化行业里专业且优秀的影像优化处理软件。 无论是从纸质文件制作高质量的影像文件&#xff0c;或是检查已经制作好的影像文件&#xff0c;锐尔文档扫描影像处理…

Generative Models from the perspective of Continual Learning【小白读论文】

摘要&#xff1a; 本文在持续学习情况下评估各种生成模型。 本文研究了几种模型如何学习和遗忘&#xff0c;并考虑了各种持续学习策略&#xff1a;回放、正则化、生成重放和微调。 我们使用两个定量指标来估计生成质量和记忆能力。 我们在三个常用的持续学习基准&#xff08;MN…

RabbitMQ08_保证消息可靠性

保证消息可靠性 一、生产者可靠性1、生产者重连机制&#xff08;防止网络波动&#xff09;2、生产者确认机制Publisher Return 确认机制Publisher Confirm 确认机制 二、MQ 可靠性1、数据持久化交换机、队列持久化消息持久化 2、Lazy Queue 惰性队列 三、消费者可靠性1、消费者…

新媒体运营

一、新媒体运营的概念 1.新媒体 2.新媒体运营的五大方向 用户运营 产品运营 。。。 二、新媒体的岗位职责及要求 三、新媒体平台

【redis-01】redis基本数据类型和使用场景

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325 redis基本数据类型和使用场景 一&#xff0c;redis基本数据类型和使用场景1&#xff0c;String数据类型2&#xff0c;Hash数据类型3&#xff…

嵌入式linux系统中rk3588芯片引脚基本操作

第一:开发板中linux系统对应设备节点 进入用户 LED 设备文件夹: 1cd /sys/class/leds/usr_led该目录下的文件分别为 brightness、device、max_brightness、power、subsystem、trigger 和 uevent,需要注意的是 brightness、max_brightness 以及 trigger 文件,这三个文件都是…

共享单车轨迹数据分析:以厦门市共享单车数据为例(五)

先来聊聊啥是共享单车电子围栏&#xff1f; 共享单车电子围栏是一种基于地理位置技术的虚拟边界&#xff0c;用于管理和规范共享单车的停放和使用。这种技术通过在地图上划定特定区域&#xff0c;帮助用户了解哪些地方可以停车&#xff0c;哪些地方不能停车&#xff0c;从而减…

网关登录校验(2)----网关如何将用户信息传递给微服务

1.微服务获取用户信息 现在&#xff0c;网关已经可以完成登录校验并获取登录用户身份信息。但是当网关将请求转发到微服务时&#xff0c;微服务又该如何获取用户身份呢&#xff1f; 由于网关发送请求到微服务依然采用的是Http请求&#xff0c;因此我们可以将用户信息以请求头…

【数据结构】假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。

编程题: 假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。 分析: 算法描述: 非递归中序遍历二叉树的算法使用栈来辅助实现。首先,从根节点开始,沿着左子树不断向下, 将每个节点压入栈中。当到达最左端节点后,开始出栈并访问节点,接着转向右子树,重…

【Kubernetes知识点】HPA如何控制不同的资源实现自动扩缩容?

【Kubernetes知识点】HPA如何控制不同的资源实现自动扩缩容&#xff1f; 目录 1 概念 1.1 什么是HPA1.2 Deployment 与 HPA 的关系 1.2.1 工作原理 1.3 StatefulSet 与 HPA 的关系 1.3.1 工作原理 2 实验案例&#xff1a;HPA 控制 StatefulSet 进行扩缩容 2.1 部署一个有状态…

7--SpringBoot-后端开发、原理详解(面试高频提问点)

目录 SpringBoot原理 起步依赖 自动配置 配置优先级 Bean设置 获取Bean 第三方Bean SpringBoot原理 内容偏向于底层的原理分析 基于Spring框架进行项目的开发有两个不足的地方&#xff1a; 在pom.xml中依赖配置比较繁琐&#xff0c;在项目开发时&#xff0c;需要自己去找…

使用Anaconda安装pyTorch

1.Anaconda简介 Anaconda 是一个流行的 Python 数据科学和机器学习平台&#xff0c;它简化了包管理和部署&#xff0c;使得安装、运行和升级包及其依赖变得非常容易。Anaconda 通过其内置的 Conda 包和环境管理器&#xff0c;提供了一个强大的环境&#xff0c;用于科学计算&…

OpenAI GPT o1技术报告阅读(3)-英文阅读及理解

✨继续阅读报告&#xff1a;使用大模型来学习推理(Reason) 原文链接&#xff1a;https://openai.com/index/learning-to-reason-with-llms/ 这次我们继续看一个英文阅读理解的案例。 原问题&#xff1a; The following passage is the draft of an excerpt from a contempora…

springboot调用python脚本实现ocr图片文字识别功能

OCR简介: OCR(Optical Character Recognition),即光学字符识别,是一种利用计算机自动识别和解析图像中的文字信息的技术。它能够将纸质文档、图片、照片等载体上的文字信息转化为计算机可编辑和处理的文本数据。 一,准备工作 1,java环境 2,springboot项目 3,python环境 …

【二级C语言考试】自定义数据类型

C语言二级考试——自定义数据类型 十、结构体&#xff08;即“结构”&#xff09;与共同体&#xff08;即“联合”&#xff09; 用 typedef说明一个新类型。结构体和共用体类型数据的定义和成员的引用。通过结构体构成链表&#xff0c;单向链表的建立&#xff0c;结点数据的输出…

Java中ArrayList和LinkedList的比较

注&#xff1a;Joshua Bloch 就是 LinkedList 的作者 在Java中&#xff0c;ArrayList和LinkedList都是常用的列表实现类&#xff0c;它们都实现了List接口&#xff0c;但在内部工作原理和性能方面有显著差异。 ArrayList&#xff1a;基于动态数组实现。随着元素的增加&#x…

小新-13 2019 Intel款IML版【81UQ】原装出厂Win10系统镜像下载

恢复lenovo联想开箱状态&#xff0c;自带预装OEM系统安装包 链接&#xff1a;https://pan.baidu.com/s/1wwPriBoIwNOAfL-YcX1F7g?pwdg7ki 提取码&#xff1a;g7ki 联想原装出厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软…

如何联系真正的开发者而非公司??

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

T9-猫狗识别2(暂时版qaq)

T9周&#xff1a;猫狗识别2 **一、前期工作**1.设置GPU,导入库2.导入数据3.查看数据 **二、数据预处理**1.加载数据2.可视化数据3.配置数据集 **三、构建CNN网络模型****四、编译模型****五、训练模型****六、模型评估****七、预测**八、总结&#xff08;暂时&#xff09; &…