动态规划之莱文斯坦距离

1,387 阅读5分钟

相信大家都有在百度或者其他的一些网站搜索的经历,有时你会发现即使输入了错误的信息,比如英语单词,搜索引擎它会很智能地辨识出错误的单词,同时将最有可能正确的英语单词提示给你,并同时进行搜索。那么,它是如何做到的呢?

莱文斯坦距离概念的提出

对于学计算机的我们都清楚地知道,用计算机来解决问题归根结底是要用数学方法来解决。那么既然是数学问题,就一定会涉及到用数字来进行量化。于是,就有科学家提出了一个非常著名的量化理论-编辑距离

编辑距离指的是一个字符串转变成另一个字符串所需要的最少编辑距离。在莱文斯坦距离中,我们只允许进行增加一个字符,删除一个字符和替换一个字符这三种操作。编辑距离越大,说明两个字符串的相似度越小;反之,编辑距离越小,说明两个字符串的相似度越大。我们的计算机就是通过这个莱文斯坦距离来进行拼写纠错的。

动态规划问题

动态规划问题通常都具有时间或空间上的次序性,因此求解这类问题时,首先要将问题按一定的次序划分成若干相互联系的阶段,以便能按一定次序去求解。

一个模型:动态规划解决的问题的模型是“多阶段决策最优解模型”。这个模型是指,将我们要解决的问题分成多个阶段,在每个阶段都要解决一个决策问题,同时在各个子阶段的决策还会影响后续阶段,这样各个阶段上的决策就会构成一个决策序列,这个序列称之为策略。策略也就是最优解。

三个特征:
<1>最优子结构:问题的最优解是所有子问题的最优解的集合。
<2>无后效性:在推导后续阶段的状态时,只关心上一个阶段的结果,并不关心上一个阶段结果的推导过程。同时,某阶段的状态一经确定,就不会受之后阶段的决策影响。
<3>重复子问题:不同的决策序列,到达某个相同的阶段时,可能会产生重复的状态。

解决动态规划问题的解题思路

  • 状态转移表法
    回溯算法的暴力搜索解决动态规划问题不失为一种方法。所以,遇到问题,先尝试用回溯算法解决,然后定义状态,状态对应节点,画出递归树。通过递归树,观察是否存在重复子问题,以及重复子问题是如何产生的,并得出规律,判断是否用动态规划解决。

  • 状态转移方程法
    首先,分析某问题如何通过子问题递归求解,也就是所谓的最优子结构。根据最优子结构写出递归公式,也就是状态转移方程。根据状态转移方程,完成代码。

回溯计算莱文斯坦距离

回溯是一个递归处理的过程。如果 a[i]与 b[j] 匹配,我们递归考察 a[i+1] 和 b[j+1]。如果 a[i] 与 b[j] 不匹配,那我们有多种处理方式可选:

  • 可以删除 a[i],然后递归考察 a[i+1] 和 b[j];
  • 可以删除 b[j],然后递归考察 a[i] 和 b[j+1];
  • 可以在 a[i] 前面添加一个跟 b[j] 相同的字符,然后递归考察 a[i] 和 b[j+1];
  • 可以在 b[j] 前面添加一个跟 a[i] 相同的字符,然后递归考察 a[i+1] 和 b[j];
  • 可以将 a[i] 替换成 b[j],或者将 b[j] 替换成 a[i],然后递归考察 a[i+1] 和 b[j+1]

将其整理成代码如下:

var a = "mitcmu";
var b = "mtacnu";
var n = 6, m = 6; 
// 回溯
var minDist = Infinity;
function lwstBT(i, j, edist) {  
  // 退出条件
  if( i == n || j == m ) {
    if (i < n) edist += (n -i); 
    if (j < m) edist += (m -j);
    if (edist < minDist) minDist = edist; 
    return ;
  } 

  if(a[i] == b[j]) {
    lwstBT(i + 1, j + 1, edist) // 两个相等, 不改变编辑距离
  } else {
    //穷举
    lwstBT(i + 1, j, edist + 1); // 删除 a[i] 或者 b[j]前添加一个字符 
    lwstBT(i, j + 1, edist + 1);  // 删除b[j] 或  a[i]前添加一个字符
    lwstBT(i + 1, j + 1, edist + 1);  // 替换为相同字符 
    // 改
  }
}
lwstBT(0, 0, 0);
console.log(minDist);

总结

用莱文斯坦距离解决拼写纠错,只是动态规划问题中的一小部分应用。回溯算法基本上可以解决动态规划问题。回溯算法相当于穷举搜索,然后对比得出最优解。时间复杂度高,适合解决小规模的数据。动态规划比回溯算法效率高,但是对问题的要求有限制,必须满足前面所讲的三个特征。

由于新手能力有限,不能对递归树和状态转移方程做太多详细的解释,就用了比较简单的回溯算法解决这个问题。望原谅!!!