《LeetCode 646 最长数对链 || LeetCode 1143 最长公共子序列》

一、题目

二、做题思路

2.1 状态表示(核心基础)

定义 dp[i] 表示以第 i 个数对结尾的最长数对链的长度 。由于数对可任意顺序选择,我们先按 left 升序排序,使得后续只需考虑 j < i 的前驱,具备无后效性。

2.2 状态转移方程(关键难点)

枚举前驱 j < i,若 pairs[i][0] > pairs[j][1] ,则可以将数对 i 接在数对 j 后面形成更长的链,长度为 dp[j] + 1。取所有合法 j 中的最大值:

dp[i] = max(dp[i], dp[j] + 1) (满足 pairs[i][0] > pairs[j][1])。

2.3 初始化(边界防护)

每个数对自身可构成长度为 1 的链,因此 dp[i] = 1 (对所有 i)。

2.4 填表顺序(递推方向)

dp[i] 依赖所有 j < i 的状态,因此 从左到右 遍历 i,内层遍历所有 j < i,确保前置状态已就绪。

2.5 返回值(目标映射)

取所有 dp[i] 中的最大值,返回该最大值

三、代码

cpp 复制代码
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        // 边界处理:空数组无摆动序列
        if (n == 0) return 0;

        // 1. 创建dp表
        // f[i] : 以 nums[i] 结尾,且最后一段比较关系为"上升"(nums[i-1] < nums[i])的摆动序列的最大长度
        // g[i] : 以 nums[i] 结尾,且最后一段比较关系为"下降"(nums[i-1] > nums[i])的摆动序列的最大长度
        // 初始化:每个元素自身可构成长度为 1 的摆动序列(既可以是上升也可以是下降的起点)
        vector<int> f(n, 1);
        vector<int> g(n, 1);

        // 2. 填表顺序:从左到右(i 从 1 到 n-1),因为 f[i]/g[i] 依赖所有 j < i 的状态
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                // 3. 状态转移方程:
                // 若 nums[i] > nums[j],则可以将 nums[i] 接在"下降"结尾的序列后面(g[j]),形成上升结尾的序列,
                // 长度 = g[j] + 1;同时保留 f[i] 原有的值(可能通过其他 j 得到更长的长度)
                if (nums[i] > nums[j]) {
                    f[i] = max(f[i], g[j] + 1);
                }
                // 若 nums[i] < nums[j],则可以将 nums[i] 接在"上升"结尾的序列后面(f[j]),形成下降结尾的序列,
                // 长度 = f[j] + 1;同时保留 g[i] 原有的值
                else if (nums[i] < nums[j]) {
                    g[i] = max(g[i], f[j] + 1);
                }
                // 若相等,无法构成摆动,跳过(状态保持不变)
            }
        }

        // 4. 返回值:所有 f[i] 和 g[i] 中的最大值
        int ret = 1;
        for (int i = 0; i < n; i++) {
            ret = max(ret, max(f[i], g[i]));
        }
        return ret;
    }
};

四、流程图

五、题目

六、做题思路

6.1 状态表示(核心基础)

本题要求返回两个字符串的最长公共子序列的长度 。定义 dp[i][j] 表示 text1 的前 i 个字符与 text2 的前 j 个字符的最长公共子序列长度

6.2 状态转移方程(关键难点)

对于 text1[i]text2[j](从1开始计数):

  • text1[i] == text2[j] ,则当前字符可纳入公共子序列,长度 = dp[i-1][j-1] + 1

  • 否则,取不包含 text1[i] 或不包含 text2[j] 的较大者,即 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

6.3 初始化(边界防护)

dp[0][j] = dp[i][0] = 0,表示空串与任意串的公共子序列长度为 0,默认初始化为 0 即可。

6.4 填表顺序(递推方向)

dp[i][j] 依赖左上 dp[i-1][j-1]、上方 dp[i-1][j] 和左方 dp[i][j-1],因此 从上到下、从左到右 遍历 ij,确保前置状态已就绪。

6.5 返回值(目标映射)

最终 返回 dp[n1][n2],即为两个完整字符串的最长公共子序列长度。

七、代码

cpp 复制代码
class Solution 
{
public:
    int longestCommonSubsequence(string text1, string text2) 
    {
        int n1 = text1.size();
        int n2 = text2.size();

        // 1. 创建dp表(多开一行一列,方便处理边界)
        // dp[i][j] 表示 text1 的前 i 个字符(text1[0..i-1])和 text2 的前 j 个字符(text2[0..j-1])的最长公共子序列长度
        vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, 0));

        // 为了便于下标对齐,在字符串前面添加一个占位符(空格),
        // 这样 text1[i] 对应原 text1 的第 i-1 个字符(i 从 1 开始)
        text1 = ' ' + text1;
        text2 = ' ' + text2;

        // 2. 初始化:dp[0][j] = 0 和 dp[i][0] = 0(默认已初始化为 0)

        // 3. 填表顺序:从上到下(i 从 1 到 n1),从左到右(j 从 1 到 n2)
        //    因为 dp[i][j] 依赖于 dp[i-1][j-1]、dp[i-1][j]、dp[i][j-1](均在左上方、上方或左方),
        //    按行优先遍历即可保证依赖状态已计算。
        for (int i = 1; i <= n1; i++) 
        {
            for (int j = 1; j <= n2; j++) 
            {
                // 4. 状态转移方程:
                // 若当前两个字符相等,则当前 LCS 长度 = 左上角 dp[i-1][j-1] + 1
                if (text1[i] == text2[j]) 
                {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } 
                else 
                {
                    // 若不等,则取上方或左方中的较大值(即去掉 text1[i] 或 text2[j] 后的 LCS)
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        // 5. 返回值:dp[n1][n2] 即为两个字符串的最长公共子序列长度
        return dp[n1][n2];
    }
};

八、流程图

相关推荐
Frostnova丶1 小时前
(15)LeetCode 189. 轮转数组
数据结构·算法·leetcode
Lumos1862 小时前
故障保护与鲁棒性(十七)
算法
阿米亚波2 小时前
【C++ STL】std::list
c++·windows·笔记·stl·list
橙色阳光五月天2 小时前
CMake 构建配置文件解析
c++·cmake·plugin
手写码匠2 小时前
从零实现大模型推理引擎:Continuous Batching 与动态调度系统深度解析
人工智能·深度学习·算法·aigc
载数而行5202 小时前
C++进阶 Linux相关的静态库,动态库,第三方库
c++
wabs6662 小时前
关于单调栈【力扣503.下一个更大元素II的思考】
算法·单调栈
c238562 小时前
下篇:伸缩魔法!进阶吃透可变滑动窗口篇
c++·算法
186******205312 小时前
RuView 新手快速上手与实战指南
算法