最长公共子序列

183 阅读2分钟

前言

“这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

1.NC92 最长公共子序列-II

NC92 最长公共子序列-II

描述

给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列

示例1

输入:

"1A2C3D4B56","B1D23A456A"

返回值:

"123456"

示例2

输入:

"abc","def"

返回值:

"-1"

复制

示例3

输入:

"abc","abc"

返回值:

"abc"

思路分析:

S1中 索引0到i的子串 为a

S2中 索引0到j的字串 为b

dp[i][j] 代表 a 和 b 之间最大公共子序列的长度

  1. 当 S1[i] == S2[j] dp[i][j] = dp[i-1][j-1] + 1;
  2. S1[i] != S2[j] 时, dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);

如下图所示, 得到 二维表之后, 从最后的位置向前反推 得到最大公共子序列

  1. 当 S1[i] == S2[j] 时, 从左上角的位置转移过来的, 记录当前字符, i--, j--
  2. 当S1[i] != S2[j] 时, 当前位置是由 上面 或左面转移过来的,
  • dp[i-1][j] >= dp[i][j-1] 时说明 从当前列的上一行转移过来的 所以 i--
  • 反之 j--

impicture_20210823_205845.png

AC 代码

import java.util.*;


public class Solution {
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    public String LCS(String s1, String s2) {
        // write code here
        
        int m = s1.length();
        int n = s2.length();
        
        if(m == 0 || n== 0){
            return "-1";
        }

        int[][] dp = new int[m][n];

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (s1.charAt(i) == s2.charAt(j)) {
                    dp[i][j] = i > 0 && j > 0 ? dp[i - 1][j - 1] + 1 : 1;
                } else {
                    int top = i > 0 ? dp[i - 1][j] : 0;
                    int left = j > 0 ? dp[i][j - 1] : 0;
                    dp[i][j] = Math.max(top, left);
                }

            }
        }
        
        if(dp[m-1][n-1] == 0){
            return "-1";
        }

        StringBuilder sb = new StringBuilder();
        int i = m - 1;
        int j = n - 1;
        while (i >= 0 && j >= 0) {
            if (s1.charAt(i) == s2.charAt(j)) {
                sb.append(s1.charAt(i));
                i--;
                j--;
                continue;
            }

            int top = i > 0 ? dp[i - 1][j] : 0;
            int left = j > 0 ? dp[i][j - 1] : 0;
            if (top >= left) {
                i--;
            } else {
                j--;
            }
        }

        return sb.reverse().toString();
    }
}