最长公共上升子序列
来源:互联网 发布:淘宝时间是北京时间吗 编辑:程序博客网 时间:2024/05/23 00:01
最长公共上升子序列
问题:
给定两个字符串x, y, 求它们公共子序列s, 满足si < sj ( 0 <= i < j < |s|).要求S的长度是所有条件序列中长度最长的.
比较直观的做法(O(n^4))
可以仿照最长上升子序列用dp[i][j], 表示以xi, yj结束的公共字串的长度.
so, 我们可以得出递推公式
if xi != yj dp[i][j] = 0else dp[i][j] = max(dp[ii][ij]) ( 0 <= ii < i, 0 <= ij < j, dp[ii][ij] != 0 && x[ii] < x[i]) + 1
时间复杂是O(n^4)
O(n^3)的算法
LICS是从LIS和LCS演变而来的.我们来看看LIS和LCS的动态规划解决方法.
在LIS中dp[i]表示以xi结束的最长上升子序列的长度.在LCS中dp[i][j]表示x[0…i]和y[0…j]的最长公共字串的长度.
为什么在LIS中dp[i]表示的不是x[0…i]中的最长子序列的长度?
因为在算LIS中dp[i]的时, 需要知道上一次字符的信息, 这样才能判断是否把x[i]加入.而在计算LCS中dp[i][j]是不需要知道上一字符的信息, 只考虑当前字符就可以.
在LICS中, 和LIS中一样, 我们需要知道上一字符的信息, dp[i][j], xi和yj就是上一字符信息, 如果xi, yi 相等, 则信息重复冗余, 我们可以试着消除冗余, 以得到一个更好的算法.
这样我们可以定义dp[i][j]表示x[0…i]和y[0…j]上的LICS, 并且在y中的结束位置为j.
so, 我们可以得到递归公式
if xi != yj dp[i][j] = dp[i-1][j]else dp[i][j] = max(dp[i-1][k])(0 < k < j && y[k] < y[j]) + 1
证明:
设x[0...m]和y[0...n]上的, 以y[n]为结束字符的最长公共上升子序列为z[0...zn].若x[m] != y[n], 则显然z[0...zn]为x[0...m-1]和y[0...n]上的, 以y[n]为结束的LICS.若x[m] == y[n], 则z[0...zn-1]必为x[0...m-1]和y[0...k]上的, 以y[k]为结束的最长的LICS( 0 < k < j), 否则会得出矛盾. 反证: 设s, s[0...sn]为x[0...m-1]和y[0...k]上的, 以x[k]为结束的一个LICS, 并且sn > zn-1. 那么,s[0...sn] 可以加上y[n], 得到长度sn+1的一个以y[n]为结束字符的最长公共上升子序列, sn+1 > zn, 与假设矛盾.所以,上述的递推公式的是对的.
时间复杂度为O(n^3).
O(n^2)对O(n^3)的一个优化.
我们看到, dp[i][j]依赖于dp[k][j-1] (0 < k < i).
在计算的时候可以把i作为外层循环,也可以把i作为内层循环.
如果把i做为外层循环的, 可以做一个优化, 把时间复杂度将为O(n^2).
O(n^3)的算法
memset(dp, 0, sizeof(dp));for (i = 1; i <= m; i++) { for(j = 1; j <= n; j++) { dp[i][j] = 0; if (x[i] != y[j]) { dp[i][j] = dp[i-1][j]; } else { for (k = 1; k < j; ++k) { if (dp[i][j] < dp[i - 1][k] && y[k] < y[j]) { dp[i][j] = dp[i - 1][k]; } } dp[i][j] += 1; }
如果优化, 就只能优化当x[i] = y[j]的时, dp[i][j]的计算.
因为现在O(n^2)个子问题,这是怎么搞也搞不掉的.
看这段代码:
for (k = 1; k < j; ++k) { if (dp[i][j] < dp[i - 1][k] && y[k] < y[j]) { dp[i][j] = dp[i - 1][k]; }}
当y[j] = x[i]时, 就等于
for (k = 1; k < j; ++k) { if (dp[i][j] < dp[i - 1][k] && y[k] < x[i]) { dp[i][j] = dp[i - 1][k]; }}
这是在求dp[i-1][k] (0 < k < j)中的满足y[k]< x[i]最大值
因为i是不变的(外层循环), j在递增, 因此没有必要从头计算.
保存一个mlen变量保存dp[i-1][k] (0 < k < j)中的满足y[k]< x[i]最大值, 当j增加时只用化O(1)的时间更新mlen和计算dp[i][j].
代码如下:
for (i = 1; i <= m; i++) { mlen = 0; for(j = 1; j <= n; j++) { dp[i][j] = dp[i-1][j]; //更新mlen if (y[j] < x[i] && dp[i - 1][j] > mlen) { mlen = dp[i - 1][j]; } //计算dp[i][j] if (y[j] == x[i]) { dp[i][j] = mlen + 1; } }}
时间复杂度O(n^2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
其实仔细想想,在最后的长度应该是:dp [m] [j] ( 0=<j<=n)中的求最大值,而不是在整个二维数组中找最大值,那样的话就浪费时间了!
练习:
- poj 2127: Greatest Common Increasing Subsequence
- hdu 4512: 吉哥系列故事——完美队形I
参考资料:
- Proverbs: POJ 2127 DP(n^2)
- 最长公共上升子序列(LCIS)的平方算法
- 最长公共上升子序列
- 最长公共上升子序列
- 最长上升公共子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长公共子上升序列
- 最长公共上升子序列
- 最长公共上升子序列
- 最长上升子序列、最长公共上升子序列
- 最长公共子序列,最长上升公共子序列
- 最长公共子序列、上升子序列、最长上升子序列、最长公共子串
- 最长上升子序列(LIS)&最长公共子序列(LCS)
- 最长上升子序列 最长公共子序列
- 倒计时
- 计算机英语词汇大全
- argc & argv in C
- 编程中良好的代码格式-如何写出一手漂亮的代码
- Python | 字符串判断及内置函数
- 最长公共上升子序列
- 深入分析 Java 中的中文编码问题
- js的垃圾回收机制
- Handlebars Paths Handlebars 路径处理 相当于html等静态资源引用的相对路径
- Requests20170928
- Python web —— Selenium 库
- python整理
- PHP与Mysql的连接
- linux个人常用命令0.1