最長公共子序列算法問題代碼(使用JavaScript實現)


問題描述

問題描述

題目描述:求得兩個數組的最長公共子序列.子序列不要求是連續的.但是要求是有的(增序).

比如字符串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)
  




參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM