1. 问题背景
假设你在开发一款智能的词典软件,例如用户想查询单词fish,但不小心输入了fosh。编辑器是否能智能提示用户fish单词呢?
假设用户输入了fosh,那用户原本想输入的是fish还是fort呢?我们先在网易有道词典上做一下实验,接下来再讲一下原理和代码实现

输入fosh提示fish

网易有道词典:输入fosh提示fish
2. 最长公共子序列问题解决原理
2.1 流程
伪代码如下
if word_a[i]==word_b[j]:cell[i][j] = cell[i-1][j-1]+1else:cell[i][j]=max(cell[i-1][j],cell[i][j-1])
以下为表格法计算最长公共子序列可以看出,
FOSH与FORT为2
FOSH与FISH为3
所以用户更可能是想输入FISH这个单词
FOSH与FORT的对比
2.2 代码实现
相比学术届的MatLab,Java、JavaScript等语言在工业界更流行, 笔者经常使用JavaScript语言进行开发,所以给出JavaScript计算最长公共子序列的JavaScript实现
/*** Create a matrix* @param {number} rows Number of rows* @param {number} columns ANumber of columns* @returns {Array} Matrix*/const createMatrix = (rows = 0, columns = 0) => {const matrix = [];for (let i = 0; i < rows; i++) {matrix[i] = Array(columns).fill(0);}return matrix;};/*** Find the longest substring* @param {string} firstWord First word* @param {string} secondWord Second word* @returns {string} The longest substring*/const longestSubstring = (firstWord = "", secondWord = "") => {const matrix = JSON.parse(JSON.stringify(createMatrix(firstWord.length, secondWord.length)));let sizeSequence = 0;let indexSequence = 0;for (let i = 0; i < firstWord.length; i++) {for (let j = 0; j < secondWord.length; j++) {if (firstWord[i] === secondWord[j]) {matrix[i][j] = (i && j) > 0 ? matrix[i - 1][j - 1] + 1 : 1;if (matrix[i][j] >= sizeSequence) {sizeSequence = matrix[i][j];indexSequence = i + 1;}} else {matrix[i][j] = 0;}}}return firstWord.slice(indexSequence - sizeSequence, indexSequence);};longestSubstring("vista", "hish"); // "is"longestSubstring("fish", "hish"); // "ish"/*** Find the longest common subsequence* @param {string} firstWord First word* @param {string} secondWord Second word* @returns {number} The longest common subsequence*/const longestCommonSubsequence = (firstWord = "", secondWord = "") => {const matrix = JSON.parse(JSON.stringify(createMatrix(firstWord.length, secondWord.length)));if (matrix.length === 0 || matrix[0].length === 0) return 0;for (let i = 0; i < firstWord.length; i++) {for (let j = 0; j < secondWord.length; j++) {if (firstWord[i] === secondWord[j]) {matrix[i][j] = (i && j) > 0 ? matrix[i - 1][j - 1] + 1 : 1;} else {matrix[i][j] = Math.max(i > 0 ? matrix[i - 1][j] : 0,j > 0 ? matrix[i][j - 1] : 0);}}}return matrix[firstWord.length - 1][secondWord.length - 1];};longestCommonSubsequence("fish", "fosh"); // 3longestCommonSubsequence("fort", "fosh"); // 2

对比CTTTGACT与ATGA的公共子序列
3. 总结
在调研了过程总发现了由谷歌出品优化工具箱,并选择了一个经典的求最长公共子序列的经典问题。解决动态规划问题的思考路径如下:
- 需要在给定约束条件下优化某种指标时,动态规划很有用
- 问题可以分解为离散子问题时,可以使用动态规划解决
- 每种动态规划解决方案都涉及网格
- 单元格中的值通常就是需要优化的值
- 每个单元格都是一个子问题,因此需要考虑如何将问题分解为子问题
- 没有放之四海皆准的计算动态规划解决方案的公式

