最长公共子序列和最长子字符串_python_算法与数据结构

1. 动态规划与分治法:

       分治法很容易划分子问题,子问题与子问题之间彼此独立,合并子问题的解很容易得到问题的解。一般采用递归来实现子问题的划分与合并这个思想。常见的问题如前面所讲的快速排序,归并排序等问题。

      动态规划解决的问题能够分为一系列的小问题,综合子问题的解推导出大问题的解,问题求解耗时会按问题规模呈幂级数增加。为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法。动态规划是一种以空间换时间的算法。

2.动态规划本身的特点:

       任何思想方法都有一定的局限性,超出了特定条件,它就失去了作用。同样,动态规划也并不是万能的。适用动态规划的问题必须满足最优化原理和无后效性。
(1)最优化原理(最优子结构性质)
      最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。最优化原理是动态规划的基础,任何问题,如果失去了最优化原理的支持,就不可能用动态规划方法计算。根据最优化原理导出的动态规划基本方程是解决一切动态规划问题的基本方法。
(2)无后向性
      将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
(3)子问题的重叠性
      动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间。
      这个性质并不是动态规划适用的必要条件,但是如果该性质无法满足,动态规划算法同其他算法相比就不具备优势。

3.动态规划的算法步骤:

设计一个标准的动态规划算法,通常可按以下几个步骤进行:
(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段一定要是有序的或者是可排序的(即无后向性),否则问题就无法用动态规划求解。
(2)选择状态:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
(3)确定决策并写出状态转移方程:之所以把这两步放在一起,是因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以,如果我们确定了决策,状态转移方程也就写出来了。但事实上,我们常常是反过来做,根据相邻两段的各状态之间的关系来确定决策。
(4)写出规划方程(包括边界条件):动态规划的基本方程是规划方程的通用形式化表达式。
一般说来,只要阶段、状态、决策和状态转移确定了,这一步还是比较简单的。动态规划的主要难点在于理论上的设计,一旦设计完成,实现部分就会非常简单。根据动态规划的基本方程可以直接递归计算最优值,但是一般将其改为递推计算,实现的大体上的框架如下:
标准动态规划的基本框架

1. 对fn+1(xn+1)初始化; {边界条件}
for k:=n downto 1 do
for 每一个xk∈Xk do
for 每一个uk∈Uk(xk) do
begin
5. fk(xk):=一个极值; {∞或-∞}
6. xk+1:=Tk(xk,uk); {状态转移方程}
7. t:=φ(fk+1(xk+1),vk(xk,uk)); {基本方程(9)式}
if t比fk(xk)更优 then fk(xk):=t; {计算fk(xk)的最优值}
end;
9. t:=一个极值; {∞或-∞}
for 每一个x1∈X1 do
11. if f1(x1)比t更优 then t:=f1(x1); {按照10式求出最优指标}
12. 输出t;
但是,实际应用当中经常不显式地按照上面步骤设计动态规划,而是按以下几个步骤进行:
(1)分析最优解的性质,并刻划其结构特征。
(2)递归地定义最优值。
(3)以自底向上的方式或自顶向下的记忆化方法(备忘录法)计算出最优值。
(4)根据计算最优值时得到的信息,构造一个最优解。
步骤(1)~(3)是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤(4)可以省略,若需要求出问题的一个最优解,则必须执行步骤(4)。此时,在步骤(3)中计算最优值时,通常需记录更多的信息,以便在步骤(4)中,根据所记录的信息,快速地构造出一个最优解。


两种动态规划算法:备忘录和迭代

动态规划一般都类似于矩阵似的图,由左下向右上解决。每一个点的解决取决于左,下和左下三个点的解决情况。

4.动态规划算法的复杂度分析:

   在上文中已经论述到动态规划是一种典型的空间换时间的解决方案,它解决的问题按照常规的算法,如搜索算法,一般是幂级数的数量级(如O(n!)),采用了动态规划,一般都能化幂级数为多项式级数。空间上动态规划算法数量级可能比常规的算法数量级要高,但这是可以承受的。

 

结合上述阐述结合LCS和LSS问题给出实例。     

假设有:

Xm = x1 x2 x3 ... xm

Yn = y1 y2 y3 ... yn

1. 最长公共子序列(不必连续)(LCS)

定义f(m, n)为Xm和Yn之间最长的子序列的长度

于是有f(m, 0) = f(0, m) = 0

如果xm != yn, 则f(m, n) = max{ f(m-1, n), f(m, n-1) }

如果xm = yn,则f(m, n) = f(m-1, n-1) + 1

问题归结于求f(m, n)。依照公式用Bottom-up Dynamic Programming可解。

 

python实现的代码:

def   LCS(x, y):
    if (len(x) == 0 or len(y) == 0):
        return 0
    else:
        a = x[0]
        b = y[0]
        if (a == b):
            return LCS(x[1:], y[1:])+1
        else:
            return cxMax( LCS(x[1:], y), LCS(x, y[1:] )  )

def cxMax(a, b):
    if (a>=b):
        return a
    else:
        return b


if  __name__ =='__main__':
    cxStra = "Study hard, Chen Xiang"
    cxStrb= "Study,Ch en "
    lista =  [] 
    listb =  []
    for i in range(0, len(cxStra) ):
        lista.append(cxStra[i])
    for  i in range(0, len(cxStrb)):
        listb.append(cxStrb[i])
    len = LCS(lista, listb)
    print('LCS Length is:{0}', len)
 
运行上述代码,结果为:

Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on CHENX, Standard
>>> LCS Length is:{0} 11

2. 最长子字符串(必须是连续的)(LSS)

定义f(m, n)为Xm和Yn之间最长的子字符串的长度并且该子字符串结束于Xm & Yn。因为要求是连续的,所以定义f的时候多了一个要求字符串结束于Xm & Yn

于是有f(m, 0) = f(0, m) = 0

如果xm != yn, 则f(m, n) = 0

如果xm = yn, 则f(m, n) = f(m-1, n-1) + 1

因为最长字符串不一定结束于Xm / Yn末尾,所以这里必须求得所有可能的f(p, q) | 0 < p < m, 0 < q < n, 最大的f(p, q)就是解。依照公式用Bottom-up Dynamic Programming可解。

def   LSS(x, y):
    if (len(x) == 0 or len(y) == 0):
        return 0
    else:
        a = x[0]
        b = y[0]
        if (a == b):
            return LSS(x[1:], y[1:])+1
        else:
            return 0

if  __name__ =='__main__':
    cxStra = "Study hard, Chen Xiang"
    cxStrb= "Study,Ch en "
    lista =  [] 
    listb =  []
    for i in range(0, len(cxStra) ):
        lista.append(cxStra[i])
    for  i in range(0, len(cxStrb)):
        listb.append(cxStrb[i])
    lenLSS = LSS(lista, listb)
    print('LSS Length is:{0}', lenLSS)

 

 
运行上述代码,结果为:

Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on CHENX, Standard
>>> LSS Length is:{0} 5

posted on 2011-03-11 16:31  追求卓越 挑战极限  阅读(4417)  评论(2编辑  收藏  举报

导航