📅  最后修改于: 2023-12-03 15:06:39.940000             🧑  作者: Mango
最长子序列问题是一类经典的动态规划问题,特别需要留意的有最长公共子序列问题(LCS,Longest Common Subsequence)以及最长上升子序列问题(LIS,Longest Increasing Subsequence)。
最长公共子序列问题是指给定两个字符串S和T,求它们的最长公共子序列。比如S=”BDCABA”和T=”ABCBDAB”,这两个字符串的最长公共子序列是”BCBA”,长度为4。
解决LCS问题常常有两种方案:
以空间换时间的方式为例,构造一个二维表dp[0…n][0…m],并令所有dp[i][0]以及dp[0][j]均为0。然后用线性的方式按顺序填写表格,并记下从表格中读取的结果。生成的最长公共子序列就是所有在填充表格中生成的矩形内的字符。
下面是一个递归方式的LCS实现,使用记忆化搜索优化。该实现虽然不如标准方法的运行时间优化,但对于LCS问题的理解至关重要。
int LCS(string s, string t, int i, int j, unordered_map<string, int>& m) {
if(i == -1 || j == -1) return 0;
string key = to_string(i) + '|' + to_string(j);
if(m.count(key)) return m[key];
if(s[i] == t[j]) return m[key] = LCS(s, t, i-1, j-1, m) + 1;
return m[key] = max(LCS(s, t, i-1, j, m), LCS(s, t, i, j-1, m));
}
int longestCommonSubsequence(string s, string t) {
unordered_map<string, int> m;
return LCS(s, t, s.size()-1, t.size()-1, m);
}
最长上升子序列问题是指给定一个序列S,找出一个最长的子序列使得其中的元素满足单调递增。比如S={10,9,2,5,3,7,101,18}的最长上升子序列是{2,3,7,101}。
常常使用动态规划来解决。设C[i]为所有长度为i的上升子序列中,最后一个数字最小的序列的最后一个元素,dp[i]则表示长度为i的上升子序列的最后一个数字的最小值。
初始情况下,C[0]=INT_MIN,dp[0]=-1。在每个位置i,使用二分搜索来找到满足j<i且C[j]<S[i]的j,之后更新C[j+1]=S[i],同时更新dp[j+1]=i。
int lengthOfLIS(vector<int>& nums) {
if(nums.empty()) return 0;
vector<int> dp(nums.size()+1, -1);
vector<int> C(nums.size()+1, INT_MAX);
C[0] = INT_MIN;
int len = 0;
for(int i = 0; i < nums.size(); i++) {
int pos = lower_bound(C.begin(), C.end(), nums[i]) - C.begin();
C[pos] = nums[i];
dp[pos] = i;
len = max(len, pos);
}
return len;
}
这段代码使用长度O(nlogn)的时间复杂度解决最长上升子序列问题。