输入:有两个字符串,如:s1=ABCADEDBF s2=ABDCEF 也可以自己输入两个字符串,或者比较两个文件内容的近似度,数据存储在两个文件中,导入到程序中。
输出:两个字符串的最长公共子序列,或输出两篇文件的近似度百分比。
思路:
- 子序列(一个给定序列中删去若干元素后得到的序列)
- 公共子序列(给定两个序列s1,s2,当另一序列Z 既是s1的子序列,又是s2 的子序列时,就称Z 为X、Y 的公共子序列
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bn-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时分为两种情况
①若am-1=bn-1,则进一步解决一个子问题,找(a0,a1,a2,...am-2)和(b0,b1,b2,....bm-2)的一个最长公共子序列
②如果am-1!=bn-1,则要解决两个子问题,找出(a0a1,..am-2)和(b0,b1,b2,...bn-1)的一个最长公共子序列,并找出(a0,a1,a2,...am-1)和(b0,b1,b2,...bn-2)的一个最长公共子序列,在取两者作为长者作为A和B的最长公共子序列
问题的递归式写成:
arr[i][j]=0 i=0或j=0 (边界条件)
arr[i][j]=arr[i-1][j-1]+1 a[i-1]=b[j-1]
arr[i][j]=MAX(arr[i][j-1],arr[i-1][j]) a[i-1]!=b[j-1]
说明:在定义arr数组时,我多定义一行边界就不需要来去判断char1[0]和char2[j]、char1[i]和char2[0]是否相等的情况。
实验代码:
//输入:
//有两个字符串,如:s1=ABCADEDBF s2=ABDCEF
//也可以自己输入两个字符串,或者比较两个文件内容的近似度,数据存储在两个文件中,导入到程序中。
//输出:两个字符串的最长公共子序列,或输出两篇文件的近似度百分比。
package cn.bxit.ALg;
import java.util.Arrays;
import java.util.Scanner;
public class maxString {
public static void main(String[] args) {
//1.输入两个字符串
Scanner scan = new Scanner(System.in);
System.out.print("请输入第一个字符串:");
String str1 = scan.next();
System.out.print("请输入第二个字符串:");
String str2 = scan.next();
//2.用两个数组来存放这两个字符串
char char1[] = str1.toCharArray();
char char2[] = str2.toCharArray();
//3.用一个二维数组arr[][]来存放公共子序列的长度
int arr[][] = new int[str1.length() + 1][str2.length() + 1];
//4.先给这个数组的行列进行初始化
// 4.1行初始化
for (int j = 0; j <= str2.length(); j++) {
arr[0][j] = 0; // 将arr[0][j]置为0,边界条件
}
// 4.2列初始化
for (int i = 0; i <= str1.length(); i++) {
arr[i][0] = 0;// 将arr[i][0]置为0,边界条件
}
// 4.3计算arr的其他元素
for (int i = 1; i <= str1.length(); i++) {
for (int j = 1; j <= str2.length(); j++) {
if (char1[i - 1] == char2[j - 1]) {
arr[i][j] = arr[i - 1][j - 1] + 1; // 情况一
} else {
arr[i][j] = Math.max(arr[i][j - 1], arr[i - 1][j]);// 情况二
}
}
}
//定义一个数来存放最大子序列长度
int lmax=Math.max(0, arr[str1.length()][str2.length()]);
//5.定义一个数组来存放最优解
char subs[] = new char[lmax];
int tempi=str1.length();
int tempj=str2.length();
int tempNumber=lmax;//定义一个临时数来使用lmax
while(tempNumber>0) {
if(arr[tempi][tempj]==arr[tempi-1][tempj]) {//左边相等
tempi--;
}else if(arr[tempi][tempj]==arr[tempi][tempj-1]){//上边相等
tempj--;
}else {
subs[tempNumber-1]=char1[tempi-1];
tempi--;tempj--;tempNumber--;
}
}
//6.输出最大公共子序列
if(lmax>0) {
System.out.println("最长公共子序列长度为:"+lmax);
System.out.print("最长公共子序列为:");
for(int i=0;i<lmax;i++) {
System.out.print(subs[i]);
}
System.out.println();
}else {
System.out.print("这两个字符串没有公共子序列。");
}
//7.比较两个字符串的相似度
int stringMax = (str1.length() > str2.length()) ? str1.length() : str2.length();
double simarity = (double) lmax / stringMax * 100;// 两个字符串的相似度
System.out.println("这两个字符串的相似度为:" + simarity + "%");
}
}