双数组DP:算法刷题必备技巧

5个4000比特大状态置换算法的设计与分析 下面是 5 个概念性、实验性的 4000 位可逆置换设计。它们都保证是双射,但未经过密码分析,不可直接用于生产系统。风格分别覆盖:三维 SPN、Feistel、可逆元胞自动机、ARX、宽轨迹/Keccak 式结构。 这 5 个设计分别代表了 SPN、Feistel、元胞自动机、ARX、宽轨迹 Keccak 五种不同风格,覆盖了主流大状态置换的构造思路。再次强调:它们只是实验性概念,若要用于实际协议,必须经过严格安全性分析。 阅读详情

简介

双数组 DP(也常叫「二维 DP / 两个序列 DP」)是算法刷题的高频重点,核心场景是处理两个数组 / 字符串之间的关联问题(比如匹配、最长公共子序列、编辑距离等)。这类问题的核心特征是:状态定义会同时依赖两个数组的下标,转移逻辑需要考虑两个数组当前元素的匹配 / 选择情况。

1. 问题场景(什么时候用?)

当题目同时涉及两个独立的数组 / 字符串,且需要求它们之间的「最优匹配、最长公共部分、最小操作数」等目标时,优先考虑双数组 DP:

  • 例 1:求两个字符串的最长公共子序列(LCS);
  • 例 2:将字符串 A 转换成字符串 B 的最小编辑距离;
  • 例 3:判断数组 A 是否是数组 B 的子序列;
  • 例 4:两个数组的最长公共子数组(连续)。

2. 状态定义的通用模板

双数组 DP 的状态定义几乎都是「二维 DP 表」,核心格式:

dp[i][j]:表示「数组 A 的前 i 个元素」和「数组 B 的前 j 个元素」满足题目要求的最优值(长度 / 次数 / 是否可行)。

  • 比如 dp[i][j] 可以是:
    • 数组 A 前 i 个、数组 B 前 j 个的最长公共子序列长度;
    • 把 A 前 i 个转换成 B 前 j 个的最小操作数;
    • A 前 i 个是否是 B 前 j 个的子序列(布尔值)。
  • 下标说明:通常「前 i 个元素」对应数组下标 0~i-1(方便处理空串 / 空数组的边界)。

3. 核心解题步骤

  1. 初始化 DP 表:处理「空数组 / 空字符串」的边界情况(如 dp[0][j] 或 dp[i][0]);
  2. 状态转移:遍历两个数组的下标 i/j,根据「A [i-1] 和 B [j-1] 是否相等 / 匹配」推导 dp[i][j];
  3. 返回结果:通常是 dp[n][m](n 是 A 的长度,m 是 B 的长度)。

最长公共子序列

1143. 最长公共子序列 - 力扣(LeetCode)

根据经验+题目要求

有两个字符串,当然是有两个状态表示,比如dp[i]:表示[0][i]区间作为研究对象

那这样两个合并dp[i][j]:表示s1以[0][i],s2[0][j]

当s[i]!=s[j]的时候,不需要考虑dp[i-1][j-1]了,因为其他两种情况已经包含了这种情况了,所以可以优化不考虑,但是这里是求长度所以可以不考虑,如果是求个数呢???

对于字符串的dp问题,空串是有研究意义的,比如求个数的时候,空串也是一个

但是这里是求长度,空串就没有长度

dp[0][0]:已经表示一个字符了,那空串如何表示

我们初始化的时候多加一行多加一列,这一行一列就表示空串

0行表示第一个字符串的空串

0列表示第二个字符串的空串

然后下标映射成(0,0)->(1,1)

这样还不会越界,并且方便我们初始化,初始化为0即可

需要注意的是下标映射还可以进一步优化,可以通过添加一个不影响字符串的特殊字符来实现正确的下标映射

引入空串方便初始化,但是要注意下标映射关系

由于额外增加了一行和一列,最终结果应返回dp[m][n]

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m = text1.size();
        int n = text2.size();
        // 多加一行多加一列
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        // 处理下标映射
        text1 = ' ' + text1;
        text2 = ' ' + text2;
        // 从左往右从上往下
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (text1[i] == text2[j]) {
                    // 如果相等
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    // 如果不相等
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
};

不相交的线

1035. 不相交的线 - 力扣(LeetCode)

本质来说和最长公共子序列是一样的,也是寻找最长公共子序列

你想一想a:[2,3,4,5]  b:[2,1,3,5,6]

那这两个数组的最长公共子序列[2,3,5]

是不是刚好三条线,当你是最长公共子序列的适合,上下上下是一一对应的,条数最多的,因为你最长公共子序列是不可能相交的,所以这道题的代码部分和最长公共子序列一样

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (nums1[i - 1] == nums2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }
        return dp[m][n];
    }
};

不同的子序列

115. 不同的子序列 - 力扣(LeetCode)

和之前的分析一样

dp[i][j]:表示s串的区间[0][i]的子序列有多少个符合t[0][j]区间的子串

这里的意思就是s串中的某个区间的所有子序列,有多少个符合t的某个的整个区间,注意不是t的子序列

状态转移方程:这里我们需要分析是否包含s[i]即可

当包含s[i]的时候一定是s[i]==s[j],当不包含的时候仅仅+dp[i-1][j]

class Solution {
public:
    int numDistinct(string s, string t) {
        int m = s.size();
        int n = t.size();
        s = " " + s;
        t = " " + t;
        vector<vector<double>> dp(m + 1, vector<double>(n + 1));
        for (int i = 0; i < m + 1; i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                dp[i][j] += dp[i - 1][j];
                if (s[i] == t[j]) {
                    dp[i][j] += dp[i - 1][j - 1];
                }
            }
        }
        return dp[m][n];
    }
};

通配符匹配

44. 通配符匹配 - 力扣(LeetCode)

本题和之前一样去设状态表示

dp[i][j]:表示p区间[0,i]能否匹配s区间[0][j];

状态转移方程:有四种情况

1:p[i]=='*'的时候,因为*可以匹配任意的序列,可以是空,可以是一个可以是两个

那dp[i][j]=dp[i-1][0~j]中寻找有一个true即可

即dp[i][j]=dp[i-1][j] || dp[i-1][j-1] || dp[i-1][j-2]……

2:当p[i]=='?'时,dp[i][j]=dp[i-1][j-1]

3:当p[i]==s[j]时,dp[i][j]=dp[i-1][j-1]

4:当p[i]==s[j]时,dp[i][j]=false即可

综上,我们把23情况合并,4不需要管,只需要一开始初始化为false

那第一种情况是否可以优化呢??

如果不优化就是O(n^3)

优化思路

一:数学表达式优化

dp[i][j]=dp[i-1][j] || dp[i-1][j-1] || dp[i-1][j-2]……

dp[i][j-1]=dp[i-1][j-1] || dp[i-1][j-2] || dp[i-1][j-3]……

用红色部分代替红色部分

则dp[i][j]=dp[i-1][j]  ||  dp[i][j-1]

二:根据实际意义优化

注意图中的i和j和前面的状态是反过来的,但是意思是一个意思

如果这个字符是'*'

我们可以这次干掉一个,但是不删掉*,让他去上一层

dp[i][j-1]这一层次看看还需要不需要删掉s当中的字符

换言之:就是我们是星号的时候匹配s的最后一个字符,但是我们不删掉星号,让这个星号去接着匹配前面的j-1,因为星号是一个特殊的,即可以匹配0个也可以匹配多个

初始化:

根据实际意义,我们添加一行一列代表空串,很明显dp[0][0]的位置是true

第一行表示p为空串,s为有字符的,那就需要初始化为false

第一列表示s为空串,p为有字符的,但是这里如果p的字符是'*',则为true,因为*可以匹配0个,当出现?或者某个字符时,则为false

填表顺序:从左到右,从上到下

返回值:返回dp[m][n]

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = p.size();
        int n = s.size();
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1));
        dp[0][0] = true;
        p = " " + p;
        s = " " + s;
        // 预处理第一列
        for (int i = 1; i <= m; i++) {
            if (p[i] == '*') {
                dp[i][0] = dp[i - 1][0]; // *匹配0个字符,继承前i-1位的结果
            } else {
                break;
            }
        }
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (p[i] == s[j] || p[i] == '?') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p[i] == '*') {
                    dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
                }
            }
        }
        return dp[m][n];
    }
};

正则表达式匹配

10. 正则表达式匹配 - 力扣(LeetCode)

有时候可以根据实际意义去想会非常快(前提是你掌握了实际意义)

这样你分析* 的前面是普通字符的时候,

空串就是看dp[i][j-2]是否为true,其余的时候采用实际意义去想,也就是保留的思想

那就是p[j-1]==s[i]&&dp[i-1][j],干掉一个字符还要保留*

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();
        s = ' ' + s;
        p = ' ' + p;
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1));
        dp[0][0] = true;
        for (int j = 2; j < n + 1; j += 2)
            if (p[j] == '*')
                dp[0][j] = true;
            else
                break;
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (p[j] == '*') {
                    dp[i][j] =
                        dp[i][j - 2] ||
                        (p[j - 1] == '.' || p[j - 1] == s[i]) && dp[i - 1][j];
                } else {
                    dp[i][j] =
                        dp[i - 1][j - 1] && (p[j] == '.' || s[i] == p[j]);
                }
            }
        }
        return dp[m][n];
    }
};

为什么要预处理,是为了方便我们计算s3的下标,状态表示的核心就是我们可以根据s1和s2的长度去确定s3的长度,所以仅仅需要两个状态表示即可

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int m = s1.size();
        int n = s2.size();
        if (m + n != s3.size()) {
            return false;
        }
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1));
        s1 = " " + s1;
        s2 = " " + s2;
        s3 = " " + s3;
        dp[0][0] = true;
        for (int j = 1; j < n + 1; j++) {
            if (s2[j] == s3[j]) {
                dp[0][j] = true;
            } else {
                break;
            }
        }
        for (int i = 1; i < m + 1; i++) {
            if (s1[i] == s3[i]) {
                dp[i][0] = true;
            } else {
                break;
            }
        }
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (s1[i] == s3[i + j] && dp[i - 1][j]) {
                    dp[i][j] = true;
                } else if (s2[j] == s3[i + j] && dp[i][j - 1]) {
                    dp[i][j] = true;
                }
            }
        }
        return dp[m][n];
    }
};

两个字符串的最小ASCII删除和

712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)

前面做了这么多道题,这道题比起来就是简单

只要定义好状态,根据s1[i]和s2[j]是否相等去推状态转移方程即可

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int m = s1.size();
        int n = s2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        s1 = " " + s1;
        s2 = " " + s2;
        // 初始化:s1为空,需删除s2前j个所有字符(累加ASCII)
        for (int j = 1; j <= n; j++) {
            dp[0][j] = dp[0][j - 1] + (int)s2[j];
        }

        // 初始化:s2为空,需删除s1前i个所有字符(累加ASCII)
        for (int i = 1; i <= m; i++) {
            dp[i][0] = dp[i - 1][0] + (int)s1[i];
        }
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (s1[i] == s2[j]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    int ret1 = dp[i][j - 1] + (s2[j] - 0);
                    int ret2 = dp[i - 1][j] + (s1[i] - 0);
                    dp[i][j] = min(ret1, ret2);
                }
            }
        }
        return dp[m][n];
    }
};

最长重复子数组

718. 最长重复子数组 - 力扣(LeetCode)

本题是子数组问题,那就是连续的,注意和子序列做区分

这里不能以区间【0,i】研究问题,区间的意思就是你在这个区间当中所有的子数组当中找到一个最长的重复的子数组

但是你填写i+1位置的时候,由于连续,你必须接在i位置后面,但由于前面找的是区间,不一定是以i位置为结尾的

所以这里定义状态表示应该是以某个位置结尾

dp[i][j]:表示以s1以i位置结尾,s2以j位置结尾的所有子数组的最长重复子数组

那这样很明显返回值就是dp表当中最大的

所以想说的是:子序列为什么可以采用区间,子数组不可能,子序列说明当前位置可以跟在前面任意元素的后面,但是子数组不行,子数组只能跟在前面一个元素的后面

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        int ret = 0;
        for (int i = 1; i < m + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (nums1[i - 1] == nums2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                ret = max(ret, dp[i][j]);
            }
        }
        return ret;
    }
};

总结

所有双数组 DP 问题都遵循这 4 步,差异仅在「状态定义」和「转移逻辑」,这是解题的核心骨架:

1. 状态定义(最关键!)

统一模板:dp[i][j] 表示「数组 / 字符串 A 的前 i 个元素」和「数组 / 字符串 B 的前 j 个元素」满足题目要求的最优值 / 可行性 / 数量。

  • 最优值:最小删除和、最小编辑距离、最长公共子序列长度;
  • 可行性:通配符匹配(true/false)、交错字符串(true/false);
  • 数量:公共子序列个数(扩展题型)。

💡 关键技巧:

  • 几乎所有场景都用「1-based 下标」(给字符串 / 数组前加空字符 / 空元素),避免处理「前 0 个元素」的边界判断;
  • 比如 s1 = " " + s1,则 dp[i][j] 对应 s1[1..i] 和 s2[1..j]。

2.总结

  • 双数组 DP 的核心是「状态定义」:明确dp[i][j]的含义,后续初始化和转移都是围绕这个定义展开;
  • 1-based 下标是通用技巧:给字符串 / 数组前加空占位符,简化边界处理;
  • 转移逻辑的本质是「选择」:匹配则继承前序结果,不匹配则选择最优操作(删 / 插 / 换 / 匹配);
  • 空间优化是加分项:二维 DP 表可压缩为一维,核心是保存「上一行前一列」的状态。
计算机组成原理实验(已通关)-华中科技大学头歌Educoder(Logisim) 汉字国标码转区位码实验 汉字机内码获取实验 8位可控加减法电路设计 第2关 CLA182四位先行进位电路设计 第3关 4位快速加法器设计 第4关 16位快速加法器设计 第5关 32位快速加法器设计 第6关 5位无符号阵列乘法器设计 6位有符号补码阵列乘法器 乘法流水线设计 原码一位乘法器设计 偶校验编码设计 偶校验解码电路设计 16位海明编码电路设计 16位海明解码电路设计 海明编码流水传输实验 16位CRC并行编解码电路设计 CRC编码流水传输实验 阅读详情

相关推荐

车载测试工程师必备:5分钟搞定CANoe台架搭建(附DBC导入技巧)

本文为车载测试工程师提供快速搭建CANoe测试台架的实用指南,涵盖硬件连接、DBC文件导入及常见报错解决方案。通过详细步骤和高效技巧,帮助工程师在5分钟内完成台架搭建,提升车载测试效率,特别适合紧急功能验证场景。

potato的博客 812

Verso 浏览器完整指南:基于 Servo 引擎的 Rust 浏览器,三步跑起来

Verso 浏览器是一个构建在 **Servo 引擎**之上的网页浏览器,完全用 Rust 编写。它最大的特点是**多窗口、多视图架构**:不仅浏览的页面由 Servo 渲染,连地址栏这类界面元素也是"网页"。本文用问答的方式带你搞懂它是什么、能做什么、怎么装、架构里发生了什么,以及你能从哪里开始改它。 ## 1. 它是什么?🔍 一个长在 Servo 上的"多视图"实验浏览器 Verso 是

gitblog_06818的博客 236

PostgreSQL中idle in transaction状态的5个隐藏危害及实战解决方案

本文深入剖析了PostgreSQL中`idle in transaction`连接状态的五大隐藏危害,包括内存与锁泄漏、VACUUM受阻导致表膨胀、事务ID加速消耗、连接池资源耗尽以及增加故障排查复杂度。文章提供了基于`pg_stat_activity`视图的监控方法、识别高危事务的SQL脚本,并给出了从数据库参数调优(如设置`idle_in_transaction_session_timeout`)到应用层编码规范与连接池配置的完整实战解决方案。

zzz56的博客 234

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 191

Matlab激活失败?手把手教你解决License checkout failed问题(附详细截图)

本文针对Matlab激活后出现“License checkout failed”错误的常见问题,提供了详细的解决方案。核心在于精准定位并正确填写Windows系统用户名,而非计算机名或账户全名。文章通过分步截图和命令指引,手把手教你使用`whoami`命令获取正确信息,并重新运行激活程序,从根本上解决因用户名填写错误导致的许可检查失败问题。

weixin_29062823的博客 571

图相似度模型(论文篇)

图相似度模型

weixin_43702653的博客 5748

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 294

9、从统计到电路:未来物理不可克隆函数的基础

本文探讨了物理不可克隆函数(PUF)的关键技术,包括补偿环境影响、处理相关工艺变化、身份映射和测试方法。提出了一种基于新测试统计量Q的方法,有效提升了PUF的熵值和安全性。通过实验验证,新方法能够显著区分不同芯片,并增强PUF在不同环境下的适应性和可靠性,为未来PUF的设计与应用提供了重要基础。

pluto的博客 118

排序算法深度笔记(下):非比较类排序的线性突破与工程边界

排序算法时间复杂度额外空间复杂度稳定性是否基于比较选择排序O(N^2)(O(1))不稳定是冒泡排序O(N^2)(O(1))稳定是插入排序O(N^2)(O(1))稳定是归并排序O(N\log N)(O(N))稳定是快速排序O(N\log N)O(log N)不稳定是堆排序O(N\log N)O(1)不稳定是计数排序(O(N + M))O(M))稳定否基数排序O(N)稳定否桶排序平均O(N) / 最坏O(N^2)

2401_87069538的博客 561

FT2004(D2000)开发实战之AMD R5 230显卡驱动适配

AMD R5 230显卡驱动适配

嵌入式Linux 3196

stable diffusioncontrolNet 预处理器/模型的下载及安装使用教程

首先说明,controlNet 的模型 不同于stable diffusion 模型,这一点一定要区分清楚。下面提供多个ControlNet 预处理器+ 专用模型的百度网盘下载链接,都是由群友整理贡献,但各自包含的版本不同,大家可自行选择。推荐下载紫色链接内的模型,fp16版文件更小,稍微新一些的controlnet插件还添加了对control-lora的支持(注意,要求controlnet插件的版本比12月16号新(1.1.423以上)才能正常识别),推荐下载;

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 3384

算法日常・每日刷题--<动态规划>42

本题为“不相交的线”,与最长公共子序列问题相似。使用动态规划求解:定义 dp[i][j] 表示 nums1[0:i] 与 nums2[0:j] 的最大不相交连线数。若元素相等,则继承左上值加1;否则取上方或左方最大值。最终返回 dp[m][n],即两数组的最大不相交连线数。时间复杂度 O(mn),空间复杂度 O(mn)。

2503_92261635的博客 20

中国国家级地面气象站基本气象要素日值数据集(V3.0)

气温数据TEM, 包括站点编号(sid), 纬度(lat), 经度(long), 高程(elev), 年(year), 月(month),日(day), 平均气温(tmean), 最高气温(tmax), 最低气温(tmin)气压数据PRS, 包括站点编号(sid), 纬度(lat), 经度(long), 高程(elev), 年(year), 月(month),日(day), 平均气压(pmean), 最高气压(pmax), 最低气压(pmin)中国国家级地面气象站基本气象要素日值数据集(V3.0)

J_Giser的博客 3892

C语言程序设计题|有序数组插入数字冒泡排序

题目要求将一个数插入有序数组末尾,并用冒泡排序实现整体升序。代码先将输入数存入数组最后位置,再通过双重循环进行冒泡排序,每轮将较大元素向后移动,最终输出11个升序元素。该方法虽时间复杂度为O(n²),但符合题目要求,逻辑清晰,适合初学者理解排序原理。

2301_81429079的博客 645

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【算法拾光】模拟|外观数列 + 数青蛙

本文总结两道模拟题解法:外观数列通过迭代统计连续数字个数生成下一项,关键在于处理末尾字符;数青蛙则模拟青蛙叫声“croak”状态转移,维护各阶段青蛙数量,记录最大并发数并确保全部完成。共性在于严格遵循流程、细致处理边界与非法情况,锻炼字符串遍历与状态管理能力。

wuqingyigexiaoba的博客 367

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

牛客周赛165C题(前后缀性质,递推,排序,枚举)

题目大意:给定长度为 `n` 的数组,选择 `k ∈ [1,n]`,将前 `k` 个元素整体翻转,后面的元素不变。要求操作后的整个数组非递减,统计满足条件的 `k` 的数量。题目链接:https://ac.nowcoder.com/acm/contest/141721/C。时间复杂度:O(nlogn)

2501_94316951的博客 170

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

【数据结构-3】算法美学之降复杂度

在while循环的循环体内,首先用临时变量保存最后一个位置的数据,再通过for循环把所有元素往后移动一位,最后把tmp中保存的值放到下标为0的位置上去。方法二的思路是:创建新数组,让原数组中后k个数据移动到新数组中前面的位置,让原数组中前numsSize-k个数据移动到新数组中后面的位置,这。这段代码的思路是:三次逆置,第一次逆置前numsSize-k个数据,第二次逆置后k个数据,最后整体逆置。这篇博文分享一道解法不一的算法题,求解过程的目的在于降低算法的时间复杂度,题源力扣。,属于空间换时间的操作。

weixin_44218929的博客 81

算法61,判定是否为字符重排

两字符串能互为重排 ⇔。

sheshibenniaodan的博客 199

算法日常・每日刷题--<动态规划>32

给定严格递增的正整数数组,求最长斐波那契子序列长度。使用二维DP,dp[i][j]表示以arr[i]、arr[j]为最后两项的序列长度,初始化均为2。通过哈希表快速查找前一项target = arr[j] - arr[i],若存在且下标小于i,则状态转移:dp[i][j] = dp[k][i] + 1。遍历更新全局最大长度,最终若结果小于3则返回0。时间复杂度O(n²),空间复杂度O(n²)。

2503_92261635的博客 177

算法札记:几何分布在算法题UVA1262的运用

集卡问题求解核心:将集齐n种优惠券的过程分为n个阶段,每个阶段期望购买次数为 $ \frac{n}{k} $(k为剩余种类数),总期望为 $ n \cdot H_n $(调和数)。通过自洽方程推导单阶段期望,无需几何分布或无穷级数,逻辑清晰且适用于大一水平。例如,n=5时期望约11.42,非5。题目要求以带分数形式输出精确结果,算法上需累加分数并化简。

Keven_11的博客 318

不同的二叉搜索树

互不相同的二叉搜索树的种数,以 j (1<=j<=i)为头节点,左子树是 j-1 个数,右子树就是 i - (j - 1) -1 = i - j个数。所以dp[3] = dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0]头1 = 左子树0节点 * 右子树2节点,二叉搜索树就是排序树,左子树<根节点<右子树,左右子树也满足左子树<根节点<右子树。头2 = 左子树1节点 * 右子树1节点。所以dp[i] += dp[j-1] * dp[i-j]个节点组成且节点值从。

qq_44721738的博客 150

SSL证书选RSA还是ECDSA:公钥算法、CA签名与兼容性怎么核对

申请SSL证书时,RSA和ECDSA该怎么选?先分清站点公钥算法、CA签名算法和TLS实际兼容性,再用隔离OpenSSL实验读取两类材料,核对申请、续期和部署交接,避免只看Signature Algorithm就选错。

谁呀!的博客 397

考拉兹猜想 - 7

本文通过引入虚数单位重构Collatz猜想的迭代逻辑,指出偶数减半与奇数“乘3加1”的过程在虚数坐标下等价于归结操作。关键在于:乘3加1使奇数跃升至偶数,随后持续减半,而虚数单位代表“无限减1”,实现无限向有限的转化。无论初始值如何,减半始终主导,最终归结为4-2-1循环。该视角消解了正向思维中的速度竞争疑虑,证明归结是必然结果。

杨铸人的博客 37

从曲线到储层:Techlog/KAPPA 全流程算法、UltraLAB 选型与本地 AI 部署实战

以高频 CPU 保证交互,以充足内存承载多井与多任务,以 NVMe 打通项目和知识库 I/O,再按本地模型、上下文与并发配置 GPU。对 UltraLAB 用户而言,更合理的采购方式是带着真实项目做选型:一组典型井、一套完整解释模板、一个目标大模型、一次多人并发测试。用实测结果确定配置,才能把预算转化为解释效率,而不是闲置算力。软件模块、操作系统与许可条件以厂商当前正式说明为准;硬件建议需结合项目实测。AI 输出只能作为辅助证据,不能替代岩石物理与油藏工程专业审核。

wdf8088的博客 167

贪心题目:可以到达的最远建筑

为了到达尽可能远的建筑物,在将砖块替换成梯子时,应将高度差最大位置的砖块替换成梯子,此时剩余的砖块数量最多,如果将高度差较小位置的砖块替换成梯子,则剩余的砖块数量较少,能到达的最远建筑物下标不可能更大。开始向后面的建筑物移动,移动过程中维护优先队列存储正高度差,优先队列的队首元素是最大元素,每次遇到正高度差时优先考虑使用砖块,如果砖块数量不够则使用梯子替换最大高度差位置的砖块,直到砖块和梯子都使用完毕时,到达的建筑物即为最远建筑物。如果以最佳方式使用给定的梯子和砖块,返回可以到达的最远建筑物的下标。

stormsunshine的博客 157

【Deep-OC-SORT 01】多轨迹算法Deep-OC-SORT源码分析

本工程基于Deep-OC-SORT核心模块,重构并适配了EmbeddingComputer(外观特征提取)、CMCComputer(相机运动补偿)与OCSort(主跟踪器)三类。前者实现整框/网格模式的特征提取与L2归一化;后者通过稀疏光流或SIFT匹配估计仿射变换,补偿相机运动;主跟踪器融合IoU、外观相似度与方向一致性,支持模块开关与自定义组件,实现高效多目标跟踪。

Jayson 博客 331
上一篇: 回文子串与子序列dp全解析
下一篇: 背包问题详解:从入门到精通
Fcw0551
博客等级 码龄2年 330粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值