問題描述
問題描述
題目描述:求得兩個數組的最長公共子序列.子序列不要求是連續的.但是要求是有的(增序).
比如字符串1:['B','D','C','A','B','A'];字符串2:['A','B','C','B','D''A','B'].
則這兩個字符串的最長公共子序列長度為4,最長公共子序列是:BCBA
- 這是一個動態規划的題目。對於可用動態規划求解的問題,一般有兩個特征:
- 最優子結構;
- 重疊子問題:
問題推理
具體分析 : 王陸.
問題抽象
- 設c[i,j]為:x[1,i],y[1,j]的最長公共子序列長度.
- 如果 x[i] == y[j].那么c[i][j] == (x[1,i-1],y[1,j-1]) +1
- 如果 x[i] != y[j].那么 c[i][j] == max( (x[1,i-1],y[1,j]) || (x[1,i],y[1,j-1]) )
- 回溯表的記錄是如果left/top/left_top表示當前元素的最優質是使用的之前的哪一個子元素.
個人感覺動態規划就是把主問題的所有子問題窮盡求出最優解.把這些解放到一張里.個人感覺動態規划一般用於求最值的情況下.
我們思考的時候是要從最底層的問題層層遞進的思考.從最簡單的事情開始推理.
圖示


代碼實現:動態規划
代碼實現
let list_a = ['A', 'B', 'C', 'B', 'D', 'A', 'B']; //序列一
let list_b = ['B', 'D', 'C', 'A', 'B', 'A'];//序列二
let table_dp = []; // 記錄各種情況的動態表
for (var i = 0; i <= list_a.length; i++) { // i是y軸
table_dp[i] = [];
for (var j = 0; j <= list_b.length; j++) { // j是x軸
table_dp[i][j] = 0;
}
}
// table_dp 二維空數組生成完畢.
let table_rec = new Array();//回溯表
for (let i = 0; i < list_a.length; i++) {
table_rec[i] = [];
for (let j = 0; j < list_b.length; j++) {
table_rec[i][j] = null;
}
}
// table_rec 二維空數組生成完畢.
// 動態規划代碼開始
for (let i = 1; i <= list_a.length; i++) { // x==0 || y==0 的情況下,均為0.所以從1開始.設置從1開始也是為了好查表.
for (j = 1; j <= list_b.length; j++) {
if (list_a[i - 1] === list_b[j - 1]) { // 兩個數組是要從下表為0開始遍歷數據的
table_dp[i][j] = table_dp[i - 1][j - 1] + 1;
table_rec[i - 1][j - 1] = 'left_top';
} else {
let max_num;
if (table_dp[i - 1][j] > table_dp[i][j - 1]) {
max_num = table_dp[i - 1][j];
table_rec[i - 1][j - 1] = 'top';
}else {
max_num = table_dp[i][j - 1];
table_rec[i - 1][j - 1] = 'left';
}
table_dp[i][j] = max_num;
}
}
}
// 動態規划代碼結束
循環回溯
// 使用while回溯
let result = [];// 記錄結果(倒序).
x_axis_length = table_rec[0].length - 1;//二維數組x軸.
y_axis_length = table_rec.length - 1;//二維數組y軸
while (x_axis_length >= 0 && y_axis_length >= 0) {
if (table_rec[y_axis_length][x_axis_length] == 'left_top') {
result.push(list_a[y_axis_length])
x_axis_length -= 1;
y_axis_length -= 1;
} else if (table_rec[y_axis_length][x_axis_length] == 'top') {
y_axis_length -= 1;
} else {
x_axis_length -= 1;
}
}
// 使用while結束
遞歸回溯
// 使用遞歸回溯
let x_axis_length_ = table_rec[0].length - 1;//二維數組x軸
let y_axis_length_ = table_rec.length - 1;//二維數組y軸
const result_recursion = [] // 記錄結果(倒序).
function recursion_rec(rec_list, dp_list, x_axis, y_axis) {
if (x_axis < 0 || y_axis < 0) {
return 0;
}
if (rec_list[y_axis][x_axis] == 'left_top') {
result_recursion.push(list_a[y_axis]);
recursion_rec(rec_list, dp_list, x_axis - 1, y_axis - 1);
} else if (rec_list[y_axis][x_axis] == 'left') {
recursion_rec(rec_list, dp_list, x_axis - 1, y_axis);
} else {
recursion_rec(rec_list, dp_list, x_axis, y_axis - 1);
}
}
// 使用遞歸結束
recursion_rec(table_rec, table_dp, x_axis_length_, y_axis_length_)
// 打印結果
console.log(table_dp, table_rec)
console.log(result);
console.log(result_recursion)
參考
- 王陸.
