最长公共子序列问题:若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
总结起来就是:1.最长公共子序列中的元素是要俩个序列共有的。
2.这些元素在原序列的下标必须的递增的。
用动态规划的思路去求解会节约许多时间,可以先求出最长公共子序列的长度,再去求出对应的值。
void LCSLength (char x[] ,char y[], int m, int n, int c[][], int b[][])
{
int i,j;
for (i = 1; i <= m; i++) c[i][0] = 0;//第0列设为0
for (i = 1; i <= n; i++) c[0][i] = 0;//第0行设为0
for (i = 1; i <= m; i++)
for (j = 1; j<= n; j++)
{
if (x[i]==y[j])
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else if (c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else
{ c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
这个方法是寻找子序列的最大长度。3个判断条件的结果,简单归纳为
[i-1,j-1] 【i-1,j】
【i,j-1】 (i,j)
假如,Xi=Yj,则是左上角b[i][j]=[i-1,j-1]+1。
如果,俩者不等,假如【i-1,j】大于【i,j-1】,则是取【上】一个值b[i][j]=【i-1,j】。
假如【i-1,j】小于【i,j-1】,则是取【左】边一个值b[i][j]=【i,j-1】。
即使俩个【】谁大,b[i][j]就等于谁的值。
当俩个序列中有第一个值相等时,就将该坐标记为1,并将这个“1”在该行传递下去,直到结束该行。
在下一行中也将把上一行的“1”继承,如果在此行中有第二个值相同时,就执行b[i][j]=[i-1,j-1]+1,
就是将本来的“1”+1,变成了2,此时的最大长度就变成了2,以此类推,直至俩个序列都找完,最后的c[i][j]就是所需要的长度。
void LCS(int i,int j, char x[] ,int b[][])
{
if (i ==0 || j==0) return;
if (b[i][j]== 1) //输出该序号
{
LCS(i-1,j-1,x,b);//【1】
System.out.print(x[i]+" ");
}
else if (b[i][j]== 2) //向上找
LCS(i-1,j,x,b);
else //b[i][j]=3,向左找
LCS(i,j-1,x,b);
}
该方法是输出子序列,是从大到小寻找。但不会倒序输出,因为当满足第一个if条件时,此时已经找到最后一个元素,
但首先会执行【1】,即已经去寻找前一个元素,以此类推,直到找到子序列的第一个元素,接着从头到尾输出,完成子序列。
源代码:
import java.util.*;
public class S3_1 {
static char x[]=new char[100];
static char y[]=new char[100];
static int c[][]=new int[100][100];
static int b[][]=new int[100][100];
int t=0;
public static void main(String[] args) {
S3_1 s=new S3_1();
Scanner sc=new Scanner(System.in);
System.out.println("输入俩个序列");
System.out.println("输入序列1、2的长度:");
int m=sc.nextInt();
int n=sc.nextInt();
System.out.println("1:");
for(int i=1;i<=m;i++) {
x[i]=sc.next().charAt(0);
}
System.out.println("2:");
for(int i=1;i<=n;i++) {
y[i]=sc.next().charAt(0);
}
s.LCSLength(x, y, m, n, c, b);
System.out.println("最长公共子序列为:");
s.LCS(m, n, x, b);
}
void LCSLength (char x[] ,char y[], int m, int n, int c[][], int b[][])
{
int i,j;
for (i = 1; i <= m; i++) c[i][0] = 0;//第0列设为0
for (i = 1; i <= n; i++) c[0][i] = 0;//第0行设为0
for (i = 1; i <= m; i++)
for (j = 1; j<= n; j++)
{
if (x[i]==y[j])
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else if (c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else
{ c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
void LCS(int i,int j, char x[] ,int b[][])
{
if (i ==0 || j==0) return;
if (b[i][j]== 1) //输出该序号
{
LCS(i-1,j-1,x,b);//【1】
System.out.print(x[i]+" ");
}
else if (b[i][j]== 2) //向上找
LCS(i-1,j,x,b);
else //b[i][j]=3,向左找
LCS(i,j-1,x,b);
}
}