1. 问题描述
子串为连续的几个字母如lo,至于什么是子序列,这里给出一个例子:有两个母串- cnblogs
- belong
比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与母串保持一致,我们将其称为公共子序列。最长公共子序列(Longest Common Subsequence,LCS),顾名思义,是指在所有的子序列中最长的那一个。子串是要求更严格的一种子序列,要求在母串中连续地出现。在上述例子的中,最长公共子序列为blog(cnblogs,belong),最长公共子串为lo(cnblogs, belong)。
2. 最长公共子序列
对于母串X=<x1,x2,⋯,xm>, Y=<y1,y2,⋯,yn>,求最长公共子序列。
假设Z=<z1,z2,⋯,zk>是X与Y的LCS, 我们观察到
如果Xm=Yn,则Zk=Xm=Yn,有Zk−1是Xm−1与Yn−1的LCS;
如果Xm≠Yn,则Zk是Xm与Yn−1的LCS,或者是Xm−1与Yn的LCS。
因此,求解LCS的问题则变成递归求解的两个子问题。但是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划(DP)的核心思想了。
DP求解LCS
用二维数组c[i][j]记录串x1x2⋯xi与y1y2⋯yj的LCS长度,则可得到状态转移方程
如果Xm=Yn,则Zk=Xm=Yn,有Zk−1是Xm−1与Yn−1的LCS;
如果Xm≠Yn,则Zk是Xm与Yn−1的LCS,或者是Xm−1与Yn的LCS。
因此,求解LCS的问题则变成递归求解的两个子问题。但是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划(DP)的核心思想了。
DP求解LCS
用二维数组c[i][j]记录串x1x2⋯xi与y1y2⋯yj的LCS长度,则可得到状态转移方程
代码如下
import java.util.Scanner;
/**
* Created by 亮大王 on 2017/10/25.
* 最长公共子序列
*/
public class LCS {
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
String str1 = sc.next();
String str2 = sc.next();
int n = str1.length();
int m = str2.length();
int [][]a = new int [n+1][m+1];
String [][]b=new String [n+1][m+1];
LCS_length(str1,str2,a,b);
LCS_print(b,str1.length(),str2.length(),str1);
}
public static void LCS_length(String str1,String str2, int[][]a,String b[][]){
for(int i = 0;i<=str2.length();i++){
a[0][i]=0;
}
for(int i=0;i<=str1.length();i++){
a[i][0]=0;
}
for(int i=1;i<=str1.length();i++)
for(int j = 1;j<=str2.length();j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
a[i][j]=a[i-1][j-1]+1;
b[i][j]="left_up";
}
else if (a[i-1][j]>a[i][j-1]){
a[i][j] = a[i-1][j];
b[i][j] = "up";
}
else {
a[i][j] = a[i][j-1];
b[i][j] = "left";
}
}
System.out.println("最长公共字串的长度为"+a[str1.length()][str2.length()]);
}
public static void LCS_print(String b[][],int n ,int m, String str1){
if(n==0||m==0)
return;
if("left_up".equals(b[n][m]))
{
LCS_print(b,n-1,m-1,str1);
System.out.print(str1.charAt(n-1));
}else if("up".equals(b[n][m])){
LCS_print(b,n-1,m,str1);
}else {
LCS_print(b,n,m-1,str1);
}
}
}
2. 最长公共子串
代码如下:
import java.util.Scanner;
/**
* Created by sunwl1993 on 2017/10/25.
*/
public class 最长公共子串 {
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
String str1 = sc.next();
String str2 = sc.next();
int n = str1.length();
int m = str2.length();
int [][]a = new int [n+1][m+1];
LCS(str1,str2,a);
}
public static void LCS(String str1,String str2,int a[][]) {
int max = 0;//记录最长子串的长度
int temp = 0;//记录最长子串的最后一位在str1中的位置
for(int i=0;i<=str1.length();i++)
a[i][0]=0;
for(int j=0;j<=str2.length();j++)
a[0][j]=0;
for(int i=1;i<=str1.length();i++)
for(int j=1;j<=str2.length();j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
a[i][j]=a[i-1][j-1]+1;
if(a[i][j]>max){
max = a[i][j];
temp = i-1;
}
}
else
a[i][j]=0;
}
System.out.println("最长公共子串的长度是"+max);
for(int i=max-1;i>=0;i--){
System.out.print(str1.charAt(temp-i));
}
}
}