LCS算法 java

最优子结构(Optimal Substructure)

最优子结构性质是指问题的最优解可以由其子问题的最优解构造而成。换句话说,如果一个问题可以分解成若干子问题,并且这些子问题的最优解能够组合成原问题的最优解,那么这个问题就具有最优子结构性质。

最长公共子序列(LCS)

最长公共子序列问题是一个典型的具有最优子结构性质的问题。它的目标是找到两个序列的最长子序列,该子序列在两个原始序列中出现的顺序一致,但不一定是连续的。例如,序列 "ABCBDAB" 和 "BDCABA" 的最长公共子序列是 "BDAB"。

动态规划解决方案(Dynamic Programming Solution)

动态规划是一种解决具有最优子结构性质问题的有效方法。对于LCS问题,动态规划的基本思想是通过构建一个二维表格来存储子问题的解,然后利用这些子问题的解来构造原问题的解。

步骤:
  1. 定义状态

    • 设 LCS(𝑖,𝑗)LCS(i,j) 为序列 𝑋[1..𝑖]X[1..i] 和 𝑌[1..𝑗]Y[1..j] 的最长公共子序列的长度。
  2. 初始化

    • LCS(𝑖,0)=0LCS(i,0)=0 对于所有的 𝑖i,因为任何序列和一个空序列的LCS长度为0。
    • LCS(0,𝑗)=0LCS(0,j)=0 对于所有的 𝑗j,因为任何序列和一个空序列的LCS长度为0。
  3. 状态转移方程

    • 如果 𝑋[𝑖]==𝑌[𝑗]X[i]==Y[j],那么 LCS(𝑖,𝑗)=LCS(𝑖−1,𝑗−1)+1LCS(i,j)=LCS(i−1,j−1)+1。
    • 如果 𝑋[𝑖]≠𝑌[𝑗]X[i]=Y[j],那么 LCS(𝑖,𝑗)=max⁡(LCS(𝑖−1,𝑗),LCS(𝑖,𝑗−1))LCS(i,j)=max(LCS(i−1,j),LCS(i,j−1))。
  4. 构建表格

    • 利用上述状态转移方程,从 LCS(1,1)LCS(1,1) 开始填充整个表格,直到 LCS(𝑚,𝑛)LCS(m,n)(其中 𝑚m 和 𝑛n 分别是序列 𝑋X 和 𝑌Y 的长度)。
  5. 构建最终解

    • 最终表格的右下角 LCS(𝑚,𝑛)LCS(m,n) 即为两个序列的最长公共子序列的长度。
    • 示例:

      假设我们有两个序列 𝑋="𝐴𝐵𝐶𝐵𝐷𝐴𝐵"X="ABCBDAB" 和 𝑌="𝐵𝐷𝐶𝐴𝐵𝐴"Y="BDCABA"。

      构建二维表格如下:

          B D C A B A
        0 0 0 0 0 0 0
      A 0 0 0 0 1 1 1
      B 0 1 1 1 1 2 2
      C 0 1 1 2 2 2 2
      B 0 1 2 2 2 3 3
      D 0 1 2 3 3 3 3
      A 0 1 2 3 3 3 4
      B 0 1 2 3 3 4 4
      

      最终表格右下角的值4表示最长公共子序列的长度为4。根据表格回溯可以得到最长公共子序列为 "BDAB"。

做题方法:

        把两个比较的字符串画在矩阵(二维数组)当中,然后第一行第一列都空着填0;

        然后从第二行(第一个字符)开始,如果行列对应字符一样的话,填入左上角数组中加1的数字在该位置,如果不一样的话就选择max(左边格子,上边格子),并都标注好是从哪个数组发展过来的箭头。

Java实现代码
public class LCS {

    // 方法:找到两个字符串的最长公共子序列
    public static String findLCS(String X, String Y) {
        int m = X.length();
        int n = Y.length();

        // 创建一个二维数组存储子问题的解
        int[][] dp = new int[m + 1][n + 1];

        // 填充二维数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (X.charAt(i - 1) == Y.charAt(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]);
                }
            }
        }

        // dp[m][n] 是两个字符串的最长公共子序列的长度
        System.out.println("Length of LCS: " + dp[m][n]);

        // 从二维数组中回溯以找到最长公共子序列
        int i = m, j = n;
        StringBuilder lcs = new StringBuilder();
        while (i > 0 && j > 0) {
            if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                lcs.append(X.charAt(i - 1));
                i--;
                j--;
            } else if (dp[i - 1][j] > dp[i][j - 1]) {
                i--;
            } else {
                j--;
            }
        }

        // 反转字符串以得到正确的顺序
        return lcs.reverse().toString();
    }

    public static void main(String[] args) {
        String X = "ABCBDAB";
        String Y = "BDCABA";
        
        String lcs = findLCS(X, Y);
        System.out.println("Longest Common Subsequence: " + lcs);
    }
}
总结

通过动态规划的方法,我们利用最优子结构性质,将一个复杂问题分解为更小的子问题,通过递推的方式逐步求解,最终得到原问题的最优解。这样的方法不仅高效,而且能确保解的正确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值