求两个字符串的最长公共子序列问题(JAVA代码详解)
最长公共子序列问题: 给定两个字符串A、B,求A与B的最长公共子序列(子序列不要求是连续的)
举例:
字符串A: abcicba
字符串B:abdkscab
其中:ab、abc、abca都是公共子序列,但是abca是最长公共子序列
从文件读取输入:
1A2C3D4B56
B1D23CA45B6A
输出:
123456
或者 12C4B6都正确
package com.bean.algorithmexec;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class LCS {
/*
* 最长公共子序列问题 给定两个字符串A、B,求A与B的最长公共子序列(子序列不要求是连续的)
* 举例:
* 字符串A: abcicba
* 字符串B:abdkscab
* 其中:ab、abc、abca都是公共子序列,但是abca是最长公共子序列
*
* 从文件读取输入:
* 1A2C3D4B56
* B1D23CA45B6A
*
* 输出:
* 123456
*
* 或者 12C4B6都正确
*
* (这里的算法其实存在一个缺陷)
*/
public static void main(String[] args) throws FileNotFoundException {
// TODO Auto-generated method stub
System.setIn(new FileInputStream("G:\\lcs.txt"));
Scanner sc = new Scanner(System.in);
String aStr = sc.nextLine(); //读取A字符串
String bStr = sc.nextLine(); //读取B字符串
int aLen = aStr.length();
int bLen = bStr.length();
//构造DP矩阵
int[][] dp = new int[aLen + 1][bLen + 1];
for (int i = 1; i < aLen + 1; i++)
for (int j = 1; j < bLen + 1; j++)
if (dp[i - 1][j] == dp[i][j - 1] && aStr.charAt(i - 1) == bStr.charAt(j - 1)
&& dp[i - 1][j] == dp[i - 1][j - 1])
dp[i][j] = dp[i - 1][j] + 1;
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
int max = dp[aLen][bLen];
StringBuilder sb = new StringBuilder();
while (max > 0) {
if (dp[aLen - 1][bLen] == dp[aLen][bLen - 1] && dp[aLen - 1][bLen] + 1 == dp[aLen][bLen]) {
sb.append(aStr.charAt(aLen - 1));
max--;
aLen--;
bLen--;
} else {
if (dp[aLen][bLen - 1] == dp[aLen][bLen])
bLen--;
else
aLen--;
}
}
System.out.println(sb.reverse().toString());
}
}
另一种算法设计
这个问题是一个经典的动态规划问题,先来介绍求解动态规划表的过程。
第1步:
如果str1的长度为M,str2的长度为N,则生成大小为M*N的矩阵dp,行数为M行,列数为N列。
d p [ i ] [ j ] dp[i][j] dp[i][j]的含义是 s t r 1 [ 0... i ] str1[0...i] str1[0...i]与 s t r 2 [ 0... j ] str2[0...j] str2[0...j]的最长公共子序列的长度。从左向右,从上向下计算矩阵dp。
其中:记str1中从第0个字符到第i个字符为 s t r 1 [ 0... i ] str1[0...i] str1[0...i];记str2中从第0个字符到第j个字符为 s t r 2 [ 0... j ] str2[0...j] str2[0...j]。
矩阵dp第一列即 d p [ 0... M − 1 ] [ 0 ] dp[0...M-1][0] dp[0...M−1][0], d p [ i ] [ 0 ] dp[i][0] dp[i][0]的含义是 s t r 1 [ 0... i ] str1[0...i] str1[0...i]与 s t r 2 [ 0 ] str2[0] str2[0]的最长公共子序列长度。
s t r 2 [ 0 ] str2[0] str2[0]只有一个字符,所以 d p [ i ] [ 0 ] dp[i][0] dp[i][0]最大为1。如果 s t r 1 [ i ] = = s t r 2 [ 0 ] str1[i]==str2[0] str1[i]==str2[0],令 d p [ i ] [ 0 ] = 1 dp[i][0]=1 dp[i][0]=1,一旦 d p [ i ] [ 0 ] dp[i][0] dp[i][0]被置为1,之后的 d p [ i + 1... M − 1 ] [ 0 ] dp[i+1...M-1][0] dp[i+1...M−1][0]也都为1。
例如: s t r 1 [ 0... M