最长公共子序列(LCS)算法
题目描述:
子序列定义:X=(x1,x2,····,xm),序列Z=(z1,z2,····,zk)是X的一子序列,必须满足:若X的索引中存在一个严格增的序列i1,i2,····,ik,使得对所有的j=1~k,均有xij=zj。
两个序列的公共子序列:Z是X和Y的子序列,则Z是两者的公共子序列CS。
最长公共子序列(LCS):在X和Y的CS中,长度最大者为一个最长公共子序列LCS。
算法思想
因为LCS问题满足最优子结构和重叠子区间,所以可用动态规划的方法来设计算法。
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)。
两个序列的最长公共子序列包含了这两个序列的前缀的最长公共子序列。因此,最长公共子序列问题具有最优子结构性质。
子问题的递归结构:
由最长公共子序列问题的最优子结构性质可知,要找出X=(x1, x2, …, xm)和Y=(y1, y2, …, yn)的最长公共子序列,可按以下方式递归地进行:当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得X和Y的一个最长公共子序列。当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。
由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。
与矩阵连乘积最优计算次序问题类似,我们来建立子问题的最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=(x1, x2, …, xi),Yj=(y1, y2, …, yj)。当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。
计算最优值:
计算最长公共子序列长度的动态规划算法LCS_LENGTH(X,Y)以序列X=(x1, x2, …, xm)和Y=(y1, y2, …, yn)作为输入。输出两个数组c[0…m ,0…n]和b[1…m ,1…n]。其中c[i,j]存储Xi与Yj的最长公共子序列的长度,b[i,j]记录指示c[i,j]的值是由哪一个子问题的解达到的,这在构造最长公共子序列时要用到。最后,X和Y的最长公共子序列的长度记录于c[m,n]中。
构造最长公共子序列
由算法LCS_LENGTH计算得到的数组b可用于快速构造序列X=(x1, x2, …, xm)和Y=(y1, y2, …, yn)的最长公共子序列。首先从b[m,n]开始,沿着其中的箭头所指的方向在数组b中搜索。当b[i,j]中遇到”↖”时,表示Xi与Yj的最长公共子序列是由Xi-1与Yj-1的最长公共子序列在尾部加上xi得到的子序列;当b[i,j]中遇到”↑”时,表示Xi与Yj的最长公共子序列和Xi-1与Yj的最长公共子序列相同;当b[i,j]中遇到”←”时,表示Xi与Yj的最长公共子序列和Xi与Yj-1的最长公共子序列相同。
代码:
计算最优解值,即计算二维数组c[][]和b[][]
递归法构造一个LCS
总结
总体而言,优化后的快速排序比普通快速排序更优,时间开销更小(除了极个别离群点外,因为离群点可能因为实验设备突发工作状态有关)。
时间开销随k值的变化的规律不是很明显,趋势不单一,更像是周期分布。
当问题规模比较大且k值也很大时,优化的快排时间开销反而比普通快排大。实验者个人认为,其原因在于此时从宏观的角度看,插入排序占据的比例较大,因此时间开销也较大。
不同的问题规模的最佳k值不同,但k的最佳取值随问题规模的增大而变小。
解题思路:
分析问题,将原问题拆分成若干子问题,能通过子问题的解推导出原问题的解,从而发现该问题可以由动态规划解决。接着采用动态规划的解题步骤,找出状态转移公式,通过公式编写代码并且重构原问题的解!