您的位置:首页 > 其它

Dynamic Programming 学习笔记(二) LIS vs LCS

2012-01-05 18:03 218 查看
LIS (Longest Increasing Subsequence) 最长上升子序列

题目:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…<sn并且这个子序列的长度最长。输出这个最长的长度。

LCS (Longest Common Subsequence) 最长公共子序列。

题目:一个数列 S,如果其分别是两个或多个已知数列的子序列,且是最长的序列,则S 称为已知序列的最长公共子序列。

LIS与LCS都是典型的DP问题。

对于LIS,有两种基本的算法。

1. 依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列1-i的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[1]=1,我们从i=2开始遍历后面的元素即可。O(N^2)复杂度。

int LIS(int arr[1000], int n)
{
for(int i=1; i<=n; ++i)
dp[i] = 0;
int max;
dp[1] = 1;
for(int i = 2; i <= n; ++i)
{
for(int j=1; j < i; ++j)
{
if(arr[i] > arr[j] && dp[j] + 1 > dp[i])
dp[i] = dp[j] + 1;
}
if (dp[i] > max)
max = dp[i];
}

return max;
}


2. 用f[k]表示长度为k的上升子序列最后一个数最小是多少。易知数组f是递增的。读到一个新的数x后,找到某个i使得x>f[i]且x<=f[i+1],于是用x去更新f[i+1];特别地,如果所有的f[i]都小于x,则增加f的长度。f的长度即为所求。由于f是递增的,因此可以采用二分查找,时间复杂度为O(nlgn)。

int bSearch(int num, int k)
{
int low=1, high=k;
while(low<=high)
{
int mid=(low+high)/2;
if(num>=b[mid])
low=mid+1;
else
high=mid-1;
}
return low;
}

int LIS_bak()
{
int low = 1, high = n;
int k = 1;
b[1] = p[1];
for(int i=2; i<=n; ++i)
{
if(p[i]>=b[k])
b[++k] = p[i];
else
{
int pos = bSearch(p[i], k);
b[pos] = p[i];
}
}
return k;
}


而对于LCS

子问题的递归结构为



分析:设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:

1. 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;

2. 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;

3. 若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。

其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。

用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。

下图能够说明这个问题。



注意:最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence, LCS)的区别:子串(Substring)是串的一个连续的部分,子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列

参考文献:

http://www.wutianqi.com/?p=1850

http://www.matrix67.com/blog/archives/112

/article/1361446.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: