动态规划——最长公共子序列(JAVA)

来源:互联网 发布:锐捷网络拓扑图素材 编辑:程序博客网 时间:2024/04/29 09:57
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。
比如两个串为:

abcicba
abdkscab

ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。
Input
第1行:字符串A第2行:字符串B(A,B的长度 <= 1000)
Output
输出最长的子序列,如果有多个,随意输出1个。
Input示例
abcicbaabdkscab
Output示例
abca

动态规划,即为自顶向下分析,自底向上设计,此题按照如下设计方式:
(设序列分别为X={x1,x2,...,xm},Y={y1,y2,...,yn})
① 当Xm = Yn时,找出Xm-1和Yn-1的最长公共子序列,再加上Xm(即Yn)
② 当Xm ≠ Yn时,找出Xm-1和Yn的一个最长公共子序列,以及找出Xm和Yn-1的一个最长公共子序列,这两个公共子序列中较长者即为X和Y的最长公共子序列
我们可以用c[i][j]来记录Xi和Yj的最长公共子序列的长度,注意边界条件。
动态转移方程如下:

这样,就容易得出代码了,还要注意的是构造子序列的方法,我们需要打印出整个子序列,可以用一个二维数组标记,再通过递归倒推出整条子序列。
import java.util.Scanner;public class Lcs {public static void Print(int i, int j, char a[], int d[][]){if(i == 0 || j == 0)return;if(d[i][j] == 1){Print(i - 1, j - 1, a, d);System.out.print(a[i - 1]);}else if(d[i][j] == 2)Print(i - 1, j, a, d);elsePrint(i, j - 1, a, d);}public static void main(String[] args) {Scanner cin = new Scanner(System.in);char a[] = new char[1000];char b[] = new char[1000];a = cin.next().toCharArray();b = cin.next().toCharArray();int l1 = a.length;int l2 =b.length;int c[][] = new int [l1+1][l2+1];int d[][] = new int [l1+1][l2+1];//边界处理c[0][0] = 0;for(int i = 1; i <= l1; i++)c[i][0] = 0;for(int i = 1; i <= l2; i++)c[0][i] = 0;//以下是动态求解的过程for(int i = 1; i <= l1; i++)for(int j = 1; j <= l2; j++){if(a[i-1] == b[j-1]){c[i][j] = c[i - 1][j - 1] +1;d[i][j] = 1;  //标记,用于构造子序列}else if(c[i][j - 1] < c[i - 1][j]){c[i][j] = c[i - 1][j];d[i][j] = 2;}else{c[i][j] = c[i][j - 1];d[i][j] = 3;}}Print(l1, l2, a, d);cin.close();}}

在Print中,每一次递归调用使i或j减少1,因此此部分算法的计算时间为O(m+n)。而在计算最优值时,由于每组数组单元的计算耗费O(1)时间,故共耗时O(mn)。