详细描述求最长公共子序列算法

子序列与字串的区别:

子序列不要求公共部分在原字符串中连续,而字串要求连续。


动态规划求解

例子:"bdcaba", "abcbdab"  最长的公共子序列为 b c b a 和 b d a b


1:首先分析状态转移数组dp[i][j]的含义:

指的是 字符串 s1 从 0到n-1 和 字符串 s2 从 0到m-1 的最大公共子序列的长度。

如上面 dp[0][0]=0 因为 s1的“b” 和s2的'a"不相等,所以最大公共子序列的长度为0

dp[0][1]=1 因为 s1的“b” 和s2的'ab"有一个字母相等,所以最大公共子序列的长度为1

以此类推以此计算 到 dp[0][m-1],再计算dp[1][0]到dp[1][m-1].......到dp[n-1][m-1]。


2:计算过程

(1)计算dp[i][j]时,先比较s1.charAt(i)是否等于s2.charAt(j),如果等于,就把dp[i][j]的长度置为dp[i-1][j-1]+1,为什么是dp[i-1][j-1]+1呢?

因为当你的当前位置(i ,j)的对应字母相等时,最大长度就是上一次的最大长度加1,而上一次的最大长度就是dp[i-1][j-1]。比如:

当比较到 dp[2][2]时,即  字符串 s1 “bdc”和 字符串 s2 "abc" 的最大公共子序列的长度。因为s1.charAt(i)等于s2.charAt(j),所以最大长度要在上一次的最大长度上加一,而上一次指的就是“bd”和"ab"也就是i-1和j-1。


(2)如果不相等,那就要判断是少一个i的长度大些呢,还是少一个j的长度大些呢,也就是判断dp[i-1][j]和dp[i][j-1]哪个大,找出大的放进dp[i][j]。


(3)最大长度肯定是dp[n-1][m-1]。因为这是完整的两个串的最大子序列长度。


3:输出

(1)因为dp数组最后一个是最大的长度,所以就从最后一个开始回溯。


(2)比较s1.charAt(i)是否等于s2.charAt(j),如果等于就是输出该字母。


(3)不等于就判断dp[i-1][j]和dp[i][j-1]哪个大,如果dp[i-1][j]大,就到他的位置,也就是j不变,i-1;否则就相反。


(4)重复(2)(3)直到i<=1&&j<=1


4: 上面的思路用图表达就是把s1和s2弄成一个二维表


5:代码

public int[][] LCS(String s1,String s2){
		
		int dp[][] = new int[s1.length()+1][s2.length()+1];
		
		for (int i = 0; i <= s1.length(); i++) {
			dp[i][0] = 0;
		}
		for (int i = 0; i <= s2.length(); i++) {
			dp[0][i] = 0;
		}
		
		for (int i = 1; i <= s1.length(); i++) {
			for (int j = 1; j <= s2.length(); j++) {
				if(s1.charAt(i-1) == s2.charAt(j-1)){ //如果相等
					dp[i][j] = dp[i-1][j-1]+1;
				}else{
					if(dp[i-1][j]>dp[i][j-1]){
						dp[i][j]=dp[i-1][j];
					}else{
						dp[i][j]=dp[i][j-1];
					}
				}
			}
		}
		
		System.out.println("最长最序列长度:"+dp[s1.length()][s2.length()]);
		
		for (int i = 0; i < dp.length; i++) {
			for (int j = 0; j < dp[0].length; j++) {
				System.out.print(dp[i][j]+" ");
			}
			System.out.println();
		}
		System.out.println("-------输出-------------");
		
		int tempS1 = s1.length();
		int tempS2 = s2.length();
		
		while(tempS1>=1 && tempS2>=1){
			if(s1.charAt(tempS1-1)==s2.charAt(tempS2-1)){
				System.out.print(s2.charAt(tempS2-1)+" ");
				tempS1--;
				tempS2--;
			}else{
				if(dp[tempS1-1][tempS2]>dp[tempS1][tempS2-1]){
					tempS1--;
				}else{
					tempS2--;
				}
			}
		}

		return dp;
	}


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值