1、问题
最长公共子序列问题:给定两个序列X={x1,x2,…,xm},Y={y1,y2,…,yn},找出X和Y的最长公共子序列。
2、算法核心
设X和Y的最长公共子序列记为LCS(X,Y),
则①:当xm=yn时,只需找LCS(Xm-1,Yn-1),并加上1,因为两序列最后元素相等xm=yn。
②:当xm≠yn时,LCS=max{LCS(Xm-1,Yn),LCS(Xm,Yn-1)},不需要加1。
③:当至少有一个序列为空时,最大公共子序列长度为0。
3、代码表达式
4、java代码
import java.util.Scanner;
public class LCSLength {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String X = in.nextLine();//定义字符串X,同时输入字符串X,类似C++的cin
String Y = in.nextLine();//定义字符串Y,同时输入字符串Y,类似C++的cout
int[][] c = new int[50][50];//创建一个数组空间
int Len1 = X.length();//分别取X和Y的长度,当然不取也行,但反复写X.length太麻烦
int Len2 = Y.length();
for (int i = 0; i < Len1; i++)//有一个字符串为空,则最大公共子序列长度必为0
c[0][i] = 0;
for (int i = 0; i < Len2; i++)//同上一句
c[i][0] = 0;
for (int i = 1; i <= Len1; i++) {//算法核心部分。为什么这里是等号呢????看下边注意!
for (int j = 1; j <= Len2; j++) {
if (X.charAt(i - 1) == Y.charAt(j - 1))
c[i][j] = c[i - 1][j - 1] + 1;//①若X与Y的最后一个字符相同,则i与j都减一,往对角线的左上角找并+1。②就是靠这个+1把数字填上的。
else
c[i][j] = Math.max((c[i - 1][j]), (c[i][j - 1]));//如果末尾两个值不相等,则i-1或j-1,即在回前一行或前一列一个位置找(上面或左边一个位置)。
}
}
for (int i = 0; i <= Len2; i++) {//第一个位置空白,Y从第二个位置开始输出。下面的X同理
if (i != 0)
System.out.print(" " + Y.charAt(i - 1));//输出数组Y的值,在第一行。 (1个空格)
else
System.out.print(" ");//(4个空格,加上面的1个空格,共5个空格 )
}
System.out.println();
for (int i = 0; i <= Len1; i++) {
if (i != 0)//故实际的X与Y是从第二个位置开始输出,X从第二列开始全部输出数组X的值。
System.out.print(" " + X.charAt(i - 1));
else//else即i=0,即第一列(数组从0开始计数),第一个位置输出空白,因为第一个位置默认为 空序列。
System.out.print(" ");//2个空格
for (int j = 0; j <=Len2;j++){
System.out.print(" "+c[i][j]);//前面1个空格,加else那里的2个空格,则此时空出3个空格,与上面对齐
}
System.out.println();
}
System.out.println();
System.out.println("LCSLength="+c[Len1][Len2]);//最长公共子序列在两个数组尽头的交汇处,所以是Len1和Len2
}
}
注意:java代码里,前半部分的先是i<Len1,后是i<=Len1,为什么一个没有等号,下一个有等号。是因为没等号的是数组,数组从0开始计数,而有等号的是字符串,字符串从1开始计数!。
5、两个参考例子的结果(java)
图解
6、C++代码
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
string X,Y;
int c[50][50];
int main(){
cin>>X>>Y;
int len1 = X.length(),len2 = Y.length();
for(int i = 1; i <= len1; i++){
c[i][0] = 0;//i=0或j=0,则有一个数组为空,那么最大公共子序列必为0
c[0][i] = 0;
}
for(int i = 1; i <= len1; i++){
for(int j = 1; j <= len2; j++){
if(X[i-1] == Y[j-1])
c[i][j] = c[i-1][j-1]+1;//如果末尾两个值相等,则i与j都减一,往对角线的左上角找/如果末尾两个值不相等,则i-1或j-1,即在回前一行或前一列一个位置找并+1。。就是靠这个+1把数字填上的
else
c[i][j] = max(c[i][j-1],c[i-1][j]);//如果末尾两个值不相等,则i-1或j-1,即在回前一行或前一列一个位置找并+1。
}
}
for(int i = 0; i <= len2; i++){//第一个位置空白,Y从第二个位置开始输出。下面的X同理
if(i!=0)
cout<<" "<<Y[i-1];//输出数组Y的值,在第一行。 (1个空格)
else
cout<<" ";//(4个空格,加上面的1个空格,共5个空格 )
}
cout<<"\n";
for(int i = 0 ; i <= len1; i++){
if(i != 0)//故实际的X与Y是从第二个位置开始输出,X从第二列开始全部输出数组X的值。
cout<<" "<<X[i-1];
else//else即i=0,即第一列(数组从0开始计数),第一个位置输出空白,因为第一个位置默认为 空序列。
cout<<" ";//2个空格
for(int j = 0; j <= len2; j++){
cout<<" "<<c[i][j];//前面1个空格,加else那里的2个空格,则此时空出3个空格,与上面对齐
}
cout<<"\n";
}
cout<<"\n";
cout<<"MaxLength = "<<c[Len1][Len2];//最长公共子序列在两个数组尽头的交汇处,所以是Len1和Len2
return 0;
}