常用算法里的细节

👨‍💻 关于作者:会编程的土豆

"不是因为看见希望才坚持,而是坚持了才看见希望。"

你好,我是会编程的土豆,一名热爱后端技术的Java学习者。

📚 正在更新中的专栏:

💕作者简介:后端学习者

LCS模版(最长公共子序列(Longest Common Subsequence))

仅求长度

  • 若字符相等:dp[i][j] = dp[i-1][j-1] + 1

  • 若字符不等:dp[i][j] = max(dp[i-1][j], dp[i][j-1])

cpp 复制代码
int longestCommonSubsequence(string text1, string text2) {
    int n = text1.size(), m = text2.size();
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
    
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (text1[i - 1] == text2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[n][m];
}

这里注意因为i,j记录的是前s的i个字符和t的前j个字符中的公共子序列,由于字符下标是从0开始的,所以实际上i只记录到下标为i-1的数,j也是同理;所以if对比的时候记得是对比si-1==tj-1;记得要减一;

回溯具体子序列

在 DP 填表完成后,从右下角 (n, m) 倒推回去

cpp 复制代码
string getLCS(string text1, string text2) {
    int n = text1.size(), m = text2.size();
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
    
    // 1. 填表
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (text1[i - 1] == text2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    
    // 2. 回溯构建 LCS 字符串
    string lcs;
    int i = n, j = m;
    while (i > 0 && j > 0) {
        // 如果字符相等,它是LCS的一部分,往左上移动
        if (text1[i - 1] == text2[j - 1]) {
            lcs.push_back(text1[i - 1]);
            i--;
            j--;
        } 
        // 如果不相等,走向数值较大的那个方向
        else if (dp[i - 1][j] > dp[i][j - 1]) {
            i--;
        } else {
            j--;
        }
    }
    
    reverse(lcs.begin(), lcs.end());
    return lcs;
}

LIS(最长上升子序列(Longest Increasing Subsequence, LIS))

这个是求一个字符串中或者数组中最长上升的那个子序列;

元素可以一样的用upper_bound

cpp 复制代码
vector<int>tail;
for (int i = 0; i < arr.size(); i++)
{
	int x = arr[i];
	auto it = upper_bound(tail.begin(), tail.end(), x);
	if (it == tail.end())
	{
		tail.push_back(x);
	}
	else *it = x;
}
cout << tail.size() << endl;

这个是求arr数组的最长可以一样元素的最长子序列;

元素不可以一样的用lower_bound

cpp 复制代码
vector<int>tailb;
for (int i = 0; i < arr.size(); i++)
{
	int x = arr[i];
	auto it = lower_bound(tailb.begin(), tailb.end(), x);
	if (it == tailb.end())
	{
		tailb.push_back(x);
	}
	else *it = x;
}
cout << tailb.size() << endl;

这个是求arr数组的最长子序列,必须严格递增;

你想要的子序列类型 应该用的函数 原因
严格递增(不能相等) lower_bound 找第一个 >= x 的位置,会替换掉相等的元素,阻止重复
非严格递增(允许相等) upper_bound 找第一个 > x 的位置,保留相等的元素,允许重复

注意里面的操作都是关于tail数组的操作,tail数组就是记录最长子序列元素的数组,而且是保证里面的每个值尽可能小;

相关推荐
MC皮蛋侠客30 分钟前
Google Test 单元测试指南
c++·单元测试·google test
艾莉丝努力练剑1 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
kkeeper~2 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
2401_868534782 小时前
论企业网络设计
数据结构
wabs6663 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964133 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
basketball6163 小时前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++
嗝o゚4 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本4 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
Fre丸子_5 小时前
自定义文件夹选取功能
c++