1. 程式人生 > 其它 >動態規劃問題(六)最長公共子序列(LCS)

動態規劃問題(六)最長公共子序列(LCS)

動態規劃問題(六)最長公共子序列(LCS)

問題描述

​ 給你兩個字串,要求得到這兩個字串的最長公共子序列長度。

​ 比如:對於輸入的字串 S1 "AGGTAB" 和 S2 "GXTXAYB",它們的最長公共子序列長度為 4,為 {'G', 'T', 'A', 'B'}

解決思路

​ 該問題剛開始見到時沒有思路,但是把問題細分一下找到規律即可解決。

  • 遞迴

    • 對於當前輸入的兩個字串,可以通過不斷將兩個字串的分別移除來減小問題的規模,最終收斂

    • 以上文的輸入為例,對於輸入的 S1 “AGGTAB” 和 S2 “GXTXAYB”,首先將 S1 的第一個字元與 S2 的第一個字元比較,然後移除 S1 的第一個字元再與 S2進行比較…… 對 S2 做同樣的操作。此時的情況如下圖所示:

  • 動態規劃

    • 動態規劃在這裡的解決的是重複子問題的型別,由於上文的遞迴方案,在這個解決過沖中存在大量的重複計算,因此可以使用動態規劃儲存中間計算結果,從而提高執行效率。

實現

  • 遞迴

    public class Solution {
        public static int lcs(String s1, String s2) {
            int len1 = s1.length(), len2 = s2.length();
    
            // 遞迴終止條件
            if (len1 == 1 || len2 == 1)
                return s1.charAt(0) == s2.charAt(0) ? 1 : 0;
    
            // 遞迴剩下的結果得到該問題的解
            if (s1.charAt(0) == s2.charAt(0))
                return Math.max(
                    lcsRecur(s1.substring(1), s2),
                    lcsRecur(s1, s2.substring(1))
                ) + 1;
    
            return Math.max(
                lcsRecur(s1.substring(1), s2),
                lcsRecur(s1, s2.substring(1))
            );
        }
    }
    
  • 動態規劃

    public class Solution {
        public static int lcs(String s1, String s2) {
            // 將字串轉變為對應的字元陣列,提高查詢的速度
        	char[] s1Arr = s1.toCharArray();
            char[] s2Arr = s2.toCharArray();
            int row = s1Arr.length, col = s2Arr.length;
    
            // 儲存中間計算結果的二維陣列
            int[][] dp = new int[row + 1][col + 1];
    
            for (int i = 1; i <= row; ++i) {
                for (int j = 1; j <= col; ++j) {
                    if (s1Arr[i - 1] == s2Arr[j - 1])
                        dp[i][j] = dp[i - 1][j - 1] + 1; // 由於要保證當前的字元是在之前比較的字元之後的,因此需要得到的是左上角的元素中間值
                    else
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
    
            return dp[row][col];
        }
    }