重要提示: 请结合王晓华老师的《算法的乐趣》章节3.3动态规划阅读此文
本文是笔者对书中章节的小小解读, 仅记录下来防止遗忘;
实际应用场景:
搜索引擎根据你输错的字符, 推荐给你正确的结果:
“Juyl” –> “July”
一. 例子: “SNOWY” –转换–> “SUNNY”
书中提到,
转换方法1(对比顺序是正向: S(头) --> Y(尾)
):
S - N O W Y
S U N N - Y
转换代价: cost = 3 (插入U, 替换O, 删除W)
转换方法2(对比顺序是逆向: Y(尾) --> S(头)
):
- S N O W - Y
S U N - - N Y
转换代价: cost = 5 (插入S, 替换S, 删除O, 删除W, 插入N)
二. 动态规划内涵及步骤
- 定义最优子问题
- 定义状态
- 定义
决策
和状态转换方程
(本人认为决策
就是状态转换方程
) - 确定边界条件
总结:
我认为动态规划更像一种”思想”而不是一种”方法”;
上面4个步骤, 根据思考的角度不同, 得出的结论也不同; 所以更类似一种指导思想
;
后文中的一种形容更像是方法: 动态规划 = 递推 + 备忘录
;
三. 几种解法
1. 朴素的递归解法:
int EditDistance(char *src, char *dest) {
if ((strlen(src) == 0 || strlen(dest) == 0))
return abs(strlen(src) - strlen(dest));
if ((src[0] == dest[0]))
return EditDistance(src + 1, dest + 1);
int edIns = EditDistance(src, dest + 1); // source插入字符
int edDel = EditDistance(src + 1, dest); // source删除字符
int edRep = EditDistance(src + 1, dest + 1); // source替换字符
return min(min(edIns, edDel), edRep) + 1;
}
乍一看, 我对朴素的递归算法时间复杂度是O(3^n)
这句话一脸懵逼,
心想, 是怎么得出这样的时间复杂度的呢??
其实从概率论的角度来思考的话,
对应到我们的例子,
1. SNOWY
和SUNNY
总共有5对字符, SS
, NU
, ON
, WN
, YY
;
2. 每一对字符, 都有3种操作, 分别是增/删/改
;
3. 题目要求是找出最小的值, 那么肯定需要遍历所有可能性;
所有的可能性有: 3 * 3 * 3 * 3 * 3 == 3^5种
; 左式如果是理解概率论的同学, 应该就很好理解了;
这种时间复杂度, 明显是不可接受的; 所以要根据动态规划的指导思想
开始改造了;
2. 添加”备忘录”
作者王晓华老师根据文章中之前提到的几个步骤, 引入动态规划
算法
因素 | 实例 |
---|---|
状态: d[i, j] | 从子串source[1…i]到子串target[1…j]之间的编辑距离 |
最优子结构 | 子问题就变为: 将source的前[1…i]个字符转换为target的[1…m]个字符所需要的最小编辑距离是什么? |
其中状态
, 说说本人的理解, 对应到实例SNOWY, SUNNY
;
1. SNOWY
可以拆成5种: S
, SN
, SNO
, SNOW
, SNOWY
2. 同理, SUNNY
拆成S
, SU
, SUN
, SUNN
, SUNNY
;
3. 上述拆分满足[1...i]
. [1...j]
的拆分规则;
4. 总共5 * 5 = 25种
状态