最长公共子序列——动态规划
什么是最长公共子序列?
最长公共子序列(LCS)是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题。一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列。最长公共子序列问题是一个经典的计算机科学问题,也是数据比较程序,比如Diff工具,和生物信息学应用的基础。它也被广泛地应用在版本控制,比如Git用来调和文件之间的改变。
代码如下:
1 package com.lzp.util; 2 3 import java.util.Scanner; 4 5 /** 6 * @Author LZP 7 * @Date 2021/3/25 19:34 8 * @Version 1.0 9 * 10 * 最大公共子序列 11 * 若有多个最大公共子序列,则将所有的情况都打印出来 12 */ 13 public class LCS { 14 private static int num = 0; 15 public static void main(String[] args) { 16 Scanner input = new Scanner(System.in); 17 int N = input.nextInt(); 18 int[] A = new int[N]; 19 int[] B = new int[N]; 20 int[][] dp = new int[N + 10][N + 10]; 21 for (int i = 0; i < N; i++) { 22 A[i] = input.nextInt(); 23 } 24 for (int i = 0; i < N; i++) { 25 B[i] = input.nextInt(); 26 } 27 28 // 找到最长公共子序列 29 // b数组用于回溯找到最长公共子序列,1代表↖ 2代表↑ 3代表← 30 int[][] b = new int[N + 10][N + 10]; 31 // 时间复杂度O(N^2) 32 for (int i = 1; i <= N; i++) { 33 for (int j = 1; j <= N; j++) { 34 if (A[i - 1] == B[j - 1]) { 35 dp[i][j] = dp[i - 1][j - 1] + 1; 36 // 来源于前一行前一列 37 b[i][j] = 1; 38 } else if (dp[i - 1][j] > dp[i][j - 1]){ 39 // 查看前面是A数列少一位之后更长,还是B数列少一位之后更长,选较长的那个作为此时的长度 40 dp[i][j] = dp[i - 1][j]; 41 // 来源于前一行 42 b[i][j] = 2; 43 } else { 44 dp[i][j] = dp[i][j - 1]; 45 // 来源于前一列 46 b[i][j] = 3; 47 } 48 } 49 } 50 51 int[] lcs = new int[N]; 52 LCS(N, N, b, lcs, A); 53 54 for (int i = 0; i < dp[N][N]; i++) { 55 System.out.printf("%d ", lcs[i]); 56 } 57 } 58 59 /** 60 * 利用递归求出最长公共子序列 61 * 时间复杂度O(n) 62 */ 63 public static void LCS(int i, int j, int[][] b, int[] lcs, int[] A) { 64 // 出口 65 if (i == 0 || j == 0) { 66 return; 67 } 68 69 if (b[i][j] == 1) { 70 LCS(i - 1, j - 1, b, lcs, A); 71 lcs[num++] = A[i - 1]; 72 } else if (b[i][j] == 2) { 73 LCS(i - 1, j, b, lcs, A); 74 } else { 75 LCS(i, j - 1, b, lcs, A); 76 } 77 } 78 }
运行结果: