[编程之美:3.3]计算字符串的相似度

来源:互联网 发布:爱剪辑for mac版 编辑:程序博客网 时间:2024/05/04 08:03

我们知道适合采用动态筹划要领的最优化疑问中的两个要素:最优子结构和重叠子疑问。另外,还有一种要领称为备忘录(memoization),可以充分运用 重叠子疑问的性质。
  下面简述一下动态筹划的基本思想。和分治法一样,动态筹划是通过组合子疑问的解而处理整个疑问的。我们知道,分治算法是指将疑问划分 成一睦独立的子疑问,递归 地求解各子疑问,然后合并子疑问的解而得到原疑问的解。与此不同,动态筹划适用于子疑问不是独立 的情况,也就是各子疑问包含公共的子子疑问。在这种情况 下,若用分治法则会做许多不必要的工作,即重复地求解公共的子子疑问。动态筹划算法对每个子子疑问只求解一次,将其结果保存在一张表中,从而防止每次遇到各个子疑问时重新计算答案。
  动态筹划通常运用 于最优化疑问。此类疑问可能有很多种可行解,每个解有一个值,而我们希望找出一个具有最优(最大或最小)值的解。称这样的解为该疑问的“一个”最优解(而不是“确定的”最优解),因为可能存在多个取最优值的解。
  动态筹划算法的设计可以分为如下4个步骤:
  1)描述最优解的结构。
  2)递归定义最优解的值。
  3)按自底向上的方式计算最优解的值。
  4)由计算出的结果构造一个最优解。
  第1~3步构成疑问的动态筹划解的基础。第4步在只要求计算最优解的值时可以略去。如果的确做了第4步,则有时要在第3步的计算中记录一些附加信息,使构造一个最优解变得容易。
  该疑问明显完全符合动态筹划的两个要素,即最优子结构和重叠子疑问特征。该疑问的最优指的是两个字符串的最短距离,子疑问的重叠性可以从原书中的那个递归算法中看出。
  下面再来细致说说什么是重叠子疑问。适用于动态筹划求解的最优化疑问必须具有的第二个要素是子疑问的空间要“很小”,也就是用来解原疑问的递归算法可以反复地解同样的子疑问,而不是总在产生新的子疑问。典型地,不同的子疑问数是输入规模的一个多项式。当一个递归算法不断地调用同一疑问时,我们说该最优疑问包含重叠子疑问。相反地,适合用分治法处理的疑问只往往在递归的每一步都产生全新的疑问。动态筹划算法总是充分运用 重叠子疑问,即通过每个子疑问只解一次,把解保存在一个须要时就可以查看的表中,而每次查表的时间为常数。
  根据以上的分析,我写了如下的动态筹划算法:

非dp算法:

dp算法:

上面的递归程序,有什么地方需要改进呢?在递归的过程中,有些数据被重复计算了。为了避免重复计算,我们可以将计算后的结果存储起来。
如下表所示

    j    C(i-1,j-1) C(i-1,j)  i  C(i,j-1) C(i,j)           

我们注意到,当我们要计算distance(A[1,i],B[1,j])时,该值仅与红色部分的值相关。因此,我们只需要保存这些值即可。