最长公共子序列LCS

Lintcode 77. 最长公共子序列

LCS问题是求两个字符串的最长公共子序列

\[ dp[i][j] =
\left\{\begin{matrix}
& max(dp[i-1][j], dp[i][j-1]), s[i] != s[j]\\
& dp[i-1][j-1] + 1, s[i] == s[j]
\end{matrix}\right.
\]

许多问题可以变形为LCS问题以求解

class Solution {
public:
/**
* @param A: A string
* @param B: A string
* @return: The length of longest common subsequence of A and B
*/
int longestCommonSubsequence(string &A, string &B) {
// write your code here
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){ if(A[i-1] == B[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[m][n];
}
};

因为dp[i][j]仅仅用到了i-1和i层的数据,因此可以用滚动数组来压缩空间,使得空间复杂度为\(O(min(m,n))\)

class Solution {
public:
/**
* @param A: A string
* @param B: A string
* @return: The length of longest common subsequence of A and B
*/
int longestCommonSubsequence(string &A, string &B) {
// write your code here
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(2, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){ if(A[i-1] == B[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
else dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-1]); }
} return dp[m%2][n];
}
};

最长递增子序列LIS

300. Longest Increasing Subsequence

动态规划

可以假定dp[i]为以nums[i]结尾的LIS长度,则dp[i] = max(dp[j] + 1)( j<i 且 nums[j] < nums[i]), 时间复杂度为\(O(n^2)\),时间复杂度为\(O(n)\)

class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
int MAX = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]) dp[i] = max(dp[j] + 1, dp[i]);
}
MAX = max(MAX, dp[i]);
} return MAX;
}
};

贪心+二分

首先我们设置一个辅助数组v,其中v[i]表示长度为i-1的LIS的末尾值,首先扫描原数组,当处理到nums[i]时和v中的数据比较,二分查找最后一个比nums[i]小的值,并更换,如果不存在,则加入到末尾,v最后的长度就是原数组LIS的长度,时间复杂度\(nlgn\)

class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> v; for(int i = 0; i < n; ++i){
auto loc = lower_bound(v.begin(), v.end(), nums[i]);
if(loc == v.end()) v.push_back(nums[i]);
else *loc = nums[i];
} return v.size();
}
};

如果仅仅是求LIS长度和允许改变原数组,空间复杂度可降低为\(O(1)\)

class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
auto p = nums.begin();
auto q = nums.begin(); for(int i = 0; i < n; i++){
auto r = lower_bound(p, q, nums[i]);
if(r == q){ ++q; }
*r = nums[i];
} return q - p;
}
};

LIS与LCS的相互转化

LIS问题可以变形为LCS问题,如输入数组为[5,1,4,2,3],最长递增子序列为[1,2,3],可以先将原数组排序得到一个新数组[1,2,3,4,5],然后新数组与原数组作为LCS的输入求解, 时间复杂度为\(O(n^2)\), 空间复杂度为\(O(n^2)\)

LCS问题也可变为LIS问题,假定输入数组为数字数组如A=[1,7,5,4,8,3,9], B=[1,4,3,5,6,2,8,9],且在A,B两个序列中每个元素各不相同(如1-n的排列),如果使用LCS求解最长公共子序列长度,则复杂度为\(O(n^2)\),A,B两个序列中每个元素各不相同,因此我们可以将A重新编码A=[1,2,3,4,5,6,7](编码不重复), B可以编码为B=[1,4,6,3,0,0,5,7](0表示不存在,也可以直接删除),然后求重新编码后A,B的LIS长度,时间复杂度为\(O(nlgn)\)

LCS与最长回文子序列LPS及变种

求S的最长回文子序列也可以使用LCS的思想,先将S反转得到S',然后求LCS(S,S')

leetcode 516. Longest Palindromic Subsequence

class Solution {
public:
int lcs(string &A, string &B) {
int n = A.size();
int m = B.size();
std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){ if(A[i-1] == B[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[m][n];
}
int longestPalindromeSubseq(string s) {
string t(s.rbegin(), s.rend());
return lcs(s, t);
}
};

变种:求在S中任何位置插入或删除最少字符个数使得S成为回文串

解法:先求最长回文子序列,然后用原长度-LPS长度

不求长度求原序列

参考

最新文章

  1. storyBoard配置错误导致崩溃 superview]: unrecognized selector...
  2. 使用C语言把字母转换成大写,不能使用库函数
  3. uiwebview加载中文URL
  4. [转]Android ORM系列之GreenDao最佳实践
  5. 11gR2数据库日志报错:Fatal NI connect error 12170、
  6. cf468A 24 Game
  7. kafka学习
  8. SuperMap iObject入门开发系列七管线横断面分析
  9. AI-2048 注释
  10. docker pull 镜像报错
  11. 你的第一个Django程序
  12. webpack工程搭建
  13. 刷完500道BAT面试题,我能去面试大厂了吗?
  14. 简单酷炫的Canvas数字时钟
  15. grep配置颜色显示
  16. G 面经 &amp;&amp; Leetcode: Longest Repeating Character Replacement
  17. selenium webdriver 实现Canvas画布自动化测试
  18. CentOS最小安装无法使用ifconfig命令
  19. Linux命令之shutdown
  20. linux学习——sed工具

热门文章

  1. Python排序搜索基本算法之归并排序实例分析
  2. MongoDB数据库数据清理
  3. 给DBGrid动态赋值后,如何用程序指定某行某列为当前焦点?(100分)
  4. 【Linux】【四】linux 删除文件
  5. SqlServer数据库查看被锁表以及解锁Kill杀死进程
  6. Spring MVC 根容器和子容器
  7. 【DSP开发】C6678的中断控制器
  8. Jenkins 远程部署
  9. mysql事件(event)
  10. Oracle表概念