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

力扣完全平方数279和力扣91解码方法的做题笔记

力扣279完全平方数

思路

1.**重述问题:

  • 给你一个数字n,拆分出它的平方数要求和为n,且这些平方数的个数要最少

  • 给定一个整数 n,找到最少数量的完全平方数,使得它们的和等于 n
    2.**找出问题的最后一步:

  • 找到以4为结尾的平方数恰好满足4 + 4 + 4 = 12,且最少

  • 假设最后一步选择了一个完全平方数 ,那么问题转化为:找到和为 n - k² 的最少完全平方数。
    3.**划分子问题:

  • 找到以4为结尾最少的平方数组合,恰好满足12 - 4

  • 对于每个 i(从 1 到 n),我们需要找到所有可能的 k² <= i),然后递归求解 i - k² 的最少完全平方数。
    4.**边界问题:

  • 考虑和不能大于n

  • i = 0 时,不需要任何完全平方数,返回 0。

  • i 本身是一个完全平方数时,直接返回 1。

  • dfs + 记忆化搜索

const int N = 100010;
class Solution
{
  public:
    int mem[N];
    int dfs(int n)
    {
        int res = 1e9;
        if (n == 0)
            return 0;
        if (mem[n])
            return mem[n];
        for (int i = 1; i * i <= n; i++)
        {
            res = min(res, dfs(n - i * i) + 1);
        }
        return mem[n] = res;
    }
    int numSquares(int n)
    {
        if (n == 0)
        {
            return 0;
        }
        int ans = dfs(n);
        return ans;
    }
};
  • dp
class Solution
{
  public:
	int numSquares(int n)
    {
        if (n == 0)
        {
            return 0;
        }
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j * j <= i; j++)
            {
                dp[i] = min(dp[i], 1 + dp[i - j * j]);
            }
        }
        return dp[n];
    }
};

力扣91解码方法

思路

1.**重述问题:

  • 给你一个数字字符串,找出它所有能支持的编码,前缀0的是非法
  • 给定一个数字字符串 s,每个数字或两个数字的组合可以解码为字母(1→A,2→B,…,26→Z)。要求计算所有可能的解码方式总数。如果字符串中包含前导零(如 “06”)或无法解码的情况,返回 0。
    2.**找出问题的最后一步:
  • 以226为例子,最后一步为 2 B 2B 6F ,最终问题是每一个字符单独成为编码
  • 对于字符串 s,解码的最后一步有两种可能:
  1. 单独解码最后一个字符:如果最后一个字符是 19,则可以单独解码为一个字母。

  2. 解码最后两个字符:如果最后两个字符组成的数字在 1026 之间,则可以解码为一个字母。

例如,对于 s = "226"

  • 最后一步可以是 6F,此时问题转化为解码 "22"

  • 最后一步也可以是 26Z,此时问题转化为解码 "2"
    3.**划分子问题:

  • 前一个问题就是22V 6F,那么子问题可划分为k个字符为一个编码?

  • 根据最后一步的两种可能,可以将问题划分为两个子问题:

  1. 解码前 n-1 个字符:如果最后一个字符可以单独解码。

  2. 解码前 n-2 个字符:如果最后两个字符可以组合解码。

例如,对于 s = "226"

  • 子问题 1:解码 "22"

  • 子问题 2:解码 "2"
    4.**边界问题:

  • 空字符串:如果字符串为空,只有一种解码方式(即空字符串本身)。

  • 单个字符

    • 如果字符是 0,无法解码,返回 0。

    • 如果字符是 19,可以解码,返回 1。

  • 前导零:如果字符串以 0 开头(如 "06"),无法解码,返回 0。

  • 无效组合:如果两个字符组成的数字不在 1026 之间(如 "27"),则不能组合解码。

const int N = 10010;
class Solution
{
  public:
    int mem[N];
    int dfs(string &s, int x)
    {
        int n = s.size();
        if (n == x)
            return 1;
        if (s[x] == '0')
            return 0;
        if (mem[x])
            return mem[x];
        // 单个解码
        int res = dfs(s, x + 1);
        // 两个解码
        if (x + 1 < n)
        {
            int two = ((s[x] - '0') * 10) + (s[x + 1] - '0');
            if (two >= 10 && two <= 26)
            {
                res += dfs(s, x + 2);
            }
        }
        return mem[x] = res;
    }
    int numDecodings(string s)
    {
        int res = 0;
        int n = s.size();
        if (n == 0 || s[0] == '0')
            return 0;
        // vector<int> dp(n + 1, 0);
        // dp[0] = 1;
        // dp[1] = s[0] != '0' ? 1 : 0;
        int prevPrev = 1;
        int prev = s[0] != '0' ? 1 : 0;
        for (int i = 2; i <= n; i++)
        {
            int cur = 0;
            if (s[i - 1] != '0')
                cur += prev;
            // dp[i] += dp[i - 1];
            int two = ((s[i - 2] - '0') * 10) + (s[i - 1] - '0');
            if (two >= 10 && two <= 26)
            {
                // dp[i] += dp[i - 2];
                cur += prevPrev;
            }
            prevPrev = prev;
            prev = cur;
        }
        return prev;
        // return dp[n];
    }
};

做题中我遇到的问题

为什么是i - 1而不是i呢

1. 动态规划的状态定义

在动态规划中,我们通常定义:

  • dp[i]:表示前 i 个字符的解码方式数。

    • 例如,dp[1] 表示前 1 个字符的解码方式数。

    • dp[2] 表示前 2 个字符的解码方式数。

    • 以此类推。


2. 字符串的索引

  • 在 C++ 中,字符串的索引是从 0 开始的。

    • 例如,字符串 s = "226"

      • s[0] = '2'

      • s[1] = '2'

      • s[2] = '6'


3. 为什么用 s[i - 1] 而不是 s[i]

  • dp[i] 表示前 i 个字符的解码方式数

    • i 个字符对应字符串中的 s[0]s[i - 1]

    • 因此,dp[i] 的解码方式数依赖于 s[i - 1](当前字符)和 s[i - 2](前一个字符)。

  • 举例说明

    • 对于字符串 s = "226"

      • dp[1] 表示前 1 个字符 '2' 的解码方式数。

      • dp[2] 表示前 2 个字符 "22" 的解码方式数。

      • dp[3] 表示前 3 个字符 "226" 的解码方式数。


4. 代码逻辑

在代码中,dp[i] 的解码方式数由两部分组成:

  1. 单独解码当前字符

    • 如果当前字符 s[i - 1] 不是 '0',则可以单独解码。

    • 此时,dp[i] += dp[i - 1],表示前 i - 1 个字符的解码方式数。

  2. 组合解码两个字符

    • 如果当前字符 s[i - 1] 和前一个字符 s[i - 2] 可以组合解码(即组成的数字在 1026 之间),则可以组合解码。

    • 此时,dp[i] += dp[i - 2],表示前 i - 2 个字符的解码方式数。


5. 示例分析

示例:s = "226"
  • 初始化

    • dp[0] = 1(空字符串)。

    • dp[1] = 1'2' 可以单独解码)。

  • 状态转移

is[i-1]单独解码 (dp[i] += dp[i-1])组合解码 (dp[i] += dp[i-2])dp[i]
2'2'dp[2] += dp[1] = 1two = 22dp[2] += dp[0] = 12
3'6'dp[3] += dp[2] = 2two = 26dp[3] += dp[1] = 13
  • 最终结果dp[3] = 3

6. 为什么不用 s[i]

  • 如果使用 s[i],会导致索引越界或逻辑错误:

    • 对于 i = 1s[1] 是第二个字符,而 dp[1] 表示前 1 个字符的解码方式数。

    • 这样会导致状态转移的逻辑混乱,无法正确计算解码方式数。


7. 总结

  • dp[i] 表示前 i 个字符的解码方式数,因此当前字符是 s[i - 1]

  • s[i - 1] 是当前字符s[i - 2] 是前一个字符。

  • 这种设计保证了状态转移的正确性和逻辑清晰性。

http://www.dtcms.com/a/47201.html

相关文章:

  • ARM Linux LCD上实时预览摄像头画面
  • VMware Ubuntu 共享目录
  • 【Go语言快速上手】第一部分:数据类型(数组、切片、映射)与控制语句
  • ​豪越科技:智慧园区后勤单位消防安全管理,实时告警与整改闭环
  • LeetCode 202. 快乐数 java题解
  • 【HTML学习笔记基础篇】
  • elpis全栈课程学习之elpis-core学习总结
  • Virtual Box虚拟机安装Mac苹果Monterey和big sur版本实践
  • UI设计——新拟态手机主题锁屏设计分享
  • vmware安装firepower ftd和fmc
  • 计算机毕业设计SpringBoot+Vue.js网上摄影工作室管理系统(源码+文档+PPT+讲解)
  • vector 面试点总结
  • 【Python机器学习】1.2. 线性回归理论:一元线性回归、最小化平方误差和公式(SSE)、梯度下降法
  • C++ 类和对象(上)
  • 使用 REINFORCE 算法强化梯度策略
  • AcWing 蛋糕游戏
  • CSS3(媒体查询、媒体类型、媒体特性、媒体运算符、BFC)
  • Pytest之parametrize参数化
  • 如何使用Spring Boot框架整合Redis:超详细案例教程
  • Java数据结构第十六期:走进二叉树的奇妙世界(五)
  • DeepSeek MLA(Multi-Head Latent Attention)算法浅析
  • Qt的坐标
  • TrustRAG:通过配置化模块化的检索增强生成(RAG)框架提高生成结果的可靠性和可追溯性
  • Redis - 高可用实现方案解析:主从复制与哨兵监控
  • drawDB:一款免费数据库设计工具
  • 从基础到进阶的Java学习技术指南
  • Spring Boot 测试:单元、集成与契约测试全解析
  • 004 rocketmq集群
  • C++杂记——RTTI
  • PageHelper新发现