一、题目

二、做题思路
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],因此 从上到下、从左到右 遍历 i 和 j,确保前置状态已就绪。
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];
}
};
八、流程图
