最长公共子序列和最长递增子序列是常见的子序列考题,非常经典。
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();}
