现在的位置: 首页 > 综合 > 正文

动态规划(6)最长公共子串

2018年03月18日 ⁄ 综合 ⁄ 共 1412字 ⁄ 字号 评论关闭

问题描述

最长公共子串(Longest Common Substring ,简称LCS)问题,是指求给定的一组字符串长度最大的共有的子串的问题。例如字符串"abcb","bca","acbc"的LCS就是"bc"。

求多串的LCS,显然穷举法是极端低效的算法。可以用动态规划算法求解。

动态规划求解

1 描述最优子结构

记Xm={x1,…xm}和Yn={y1,…,yn}为两个字符串,而Zk={z1,…zk}是它们的最长公共子串,则:

1) 如果xm=yn,那么zk=xm=yn,并且Zk-1是Xm-1和Yn-1的一个最长公共子串
2)如果xm≠yn,那么当zk为空

2 递归定义最优解的值

有了上面的性质,我们可以得出如下的思路:求两字符串Xm={ x1,…xm}和Yn={y1,…,yn}的LCS,如果xm=yn,那么只需求得Xm-1和Yn-1最长公共子串,并在其后添加xm(yn)即可;如果xm-1≠yn-1,那么他们的最长公共子串为0。

记字符串Xi和Yj的一个最长公共子串的长度为c[i,j]。如果i=0或j=0,其中一个的长度为0,因此最长公共子串的长度为0。由最长公共子串问题的最优子结构可得递归式:

          / 0                                if i=0 or j=0
c[i,j]=     c[i-1,j-1]+1             if i,j>0 and xi=yj
         \     0                             if i,j>0 and xi≠yj

3 按自底向上方式计算最优解的值

    上面的公式用递归函数不难求得。但直接递归(自顶向下)会有很多重复计算,我们用从底向上循环求解的思路效率更高。

为了能够采用循环求解的思路,求取c[i,j]可以从c[i-1,j-1] 这个方向计算得到,相当于在矩阵c[0...m,0...n]中是从c[i-1,j-1]这个方向移动到c[i,j],因此在矩阵中c[i,j]回溯到c[i-1,j-1]的方向是左上方,因为c中值不为0的地方的回溯只有这一个方向,所以不需要用另外一个矩阵b[1..m,1..n]来保存移动的方向。以字符串X=“BABA”和Y=“ABAB”为例,说明这个过程:

第一步初始化c[0,j]和c[i,0]


根据递归公式给c数组赋值如下


根据每一步做出选择的过程(图中箭头)和c中最大值,可以回溯构造出最优解,由此得到的最大公共子串为“BAB”和"ABA"



伪代码如下:

LongestCommonSubstring(x,y){ 
 //初始化 i=0 或j=0
 for i=0 to n
    c[i,0]=0;
 for j=0 to m
    c[0,j]=0;

 for i=1 to m{
     for j=1 to n{
	    if(x[i]=y[j])
		   c[i,j]=c[i-1,j-1]+1;
		else
		   c[i,j]=0;
	 }//end for
 }//end for
}//end LongestCommonSubstring()

算法时间复杂度为O(mn)。

4 由计算出的结果构造一个最优解

由返回的表b和最大值可以用来快速构造Xm={ x1,…xm}Yn={y1,…,yn}的一个最长公共子串。首先在c中找到最大值的位置,然后从这个位置处开始,沿着当前箭头指向在表格中找到下一个位置,由此跟踪下去。

/**
*i,j为最大值在c中的位置
*/
Print-LCS(b,X,i,j)
  //base case
  if i=0 或 j=0
     return
  else
     then Print-LCS(b,X,i-1,j-1)


抱歉!评论已关闭.