最长公共子序列和最长递增子序列是常见的子序列考题,非常经典。
LCS的状态转移方程和Edit distance几乎一样,如果要回溯LCS,则需要一个容器记录路径即可。
int LCS(const string& s, const string& t,string& lcs) {
if (s.empty() || t.empty()) return -1;
int m = s.size();
int n = t.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
vector<vector<string>> map(m + 1, vector<string>(n + 1, "."));
for(int i=1;i<=m;++i){
for (int j = 1; j <= n; ++j) {
if (s[i - 1] == t[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
map[i][j] = "\\";
}
else if (dp[i-1][j]>=dp[i][j-1])
{
dp[i][j] = dp[i - 1][j];
map[i][j] = "|";
}else {
dp[i][j] = dp[i][j - 1];
map[i][j] = "-";
}
}
}
if (dp[m][n] == 0)
return 0;
//回溯出LSC总串
int i = m, j = n;
while (i > 0 && j > 0) {
if (map[i][j] == "\\") {
lcs.push_back(s[i-1]);
--i;
--j;
}
else if (map[i][j] == "|")
--i;
else
--j;
}
reverse(lcs.begin(), lcs.end());
return dp[m][n];
}
LIS不要求回溯出子序列,则比较简单,用单调栈+二分搜索。
int lis(vector<int>& arr){
vector<int> stk;//递增栈
for(int i=0;i<arr.size();++i){
if(stk.empty()||stk.back()<arr[i]){//因为严格递增,所以等于无法入栈
stk.emplace_back(arr[i]);
}else{
*lower_bound(stk.begin(),stk.end(),arr[i])=arr[i];
}
}
return stk.size();
}