动态规划-最长公共子序列问题

输入:有两个字符串,如:s1=ABCADEDBF    s2=ABDCEF  也可以自己输入两个字符串,或者比较两个文件内容的近似度,数据存储在两个文件中,导入到程序中。

输出:两个字符串的最长公共子序列,或输出两篇文件的近似度百分比。

思路:

  1. 子序列(一个给定序列中删去若干元素后得到的序列)
  2. 公共子序列(给定两个序列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 + "%");
		
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平胸瑶女神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值