`

递归和动态规划构造两个字符序列的最长公共字符子序列

 
阅读更多
    应je朋友要求,所以翻开以前的算法题目,整理了以下,给出主题的递归和非递归(动态规划)实现。如果能有所帮助,甚是欣慰。我们在JE上更需要分享精神。

    je上有位朋友,给我发短信说,"你为什么有段时间每天都写博客发帖子,你在做什么工作?" 其实,我是搞J2EE的。和大家一样都是Java程序员。我想说的是,"学了计算机,做为一名程序员,他会花费大部分时间和电脑,代码相处,他需要不断的学习,他能真正体会到学无止境是一种什么境界...", 所以,在别人眼里,他们有很少的时间出去花天酒地,出去散心,出去享受这个世界的精彩,因此他们是孤独的,他们要想在自己的职业中有所进步,他们必须忍受孤独...

   孤独了怎么办?内心有想法怎么办?和非同行交流,一头雾水,这是什么啊,不懂...和同行交流,最好的平台在哪里? 在社区...

   于是,我便有了一个小小的目标,我喜欢学习,但我更喜欢分享。因为分享,我才会如此快乐。才会如此发现自己的不足和缺点。才会发现他人的优点,他人的品质。

   于是,我更想做一名服务者,服务大家,向大家学习,职业生涯才会充满乐趣,而非枯燥。也许,会某个问题会和大家有所争执, 但请大家明白,一切行为不是针对人,而是问题本身。当然,我会更加规范和约束自己的行为,虚心倾听,诚心交流和分享。

   每位程序员都不容易,他们的思维都是很有创造性的,他们消耗的能量是最多的,所以每位程序员更需要一个和谐,平衡,安宁的环境。

   我相信,在那些高手,大师所达到的境界里,也许技术不是最重要的,最重要的是内在的和谐,内在的思想,学习的方法和分享的精神。


1. 题目: 求两个字符序列的最长公共字符子序列。

2. 问题分析:

   (1) 递推关系分析:

       考虑最长公共子序列如何变为较小的子问题。
       设A = "a0a1...a(i-1)"
         B = "b0b1...b(j-1)"
       且C = "c0c1...c(k-1)"为A和B的最长公共子序列。


       不难证明有以下结论:

      <1> 如果a(i-1) = b(j-1), 则c(k-1) = a(i-1) = b(j-1), 因此,"c0c1...c(k-2)"是"a0a1...a(i-2)"和"b0b1...b(j-2)"
的一个最长公共子序列。

       <2> 如果a(i-1) != b(j-1), 若c(k-1) = a(i-1), 说明"c0c1...c(k-1)"是"a0a1...a(i-2)"和"b0b1...b(j-1)"
的一个最长公共子序列。

       <3> 如果a(i-1) != b(j-1), 若c(k-1) = b(j-1), 说明"c0c1...c(k-1)"是"a0a1...a(i-1)"和"b0b1...b(j-2)"
的一个最长公共子序列。


   (2) 存储及子问题合并
       基本的存储结构需要3个一维数组,分别存放A、B和C字符序列。要找出最长公共子序列,最重要的是存储当前最长公共子序列的长度
和当前公共子序列的长度。 开辟(i+1)*(j+1)二维数组c,用c[i][j]存储"a0a1...a(i-1)"和"b0b1...b(j-1)"最长公共子序列的长度。由上面
的递推关系,计算出c[i][j]的递归程式:


      <1> c[i][j] = 0  (如果i=0或j=0)

      <2> c[i][j] = c[i - 1][j - 1] + 1 (如果i、j>0且a(i-1) = b(i-1))

      <3> c[i][j] = max(c[i][j-1], c[i-1][j]) (如果i、j>0且a(i-1) != b(i-1))

3. 代码

   (1) 动态规划递归代码:

package boke.written;

/**
 * 求两个字符串的最长公共字符子序列
 * 
 * @since jdk1.5及其以上
 * @author 毛正吉
 * @version 1.0
 * @date 2010.06.07
 * 
 */
public class MaxLenPubSequence {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String a = "ABCBDAB";
		String b = "BDCABA";
		MaxLenPubSequence mps = new MaxLenPubSequence(a, b);
		mps.process();

	}

	private int n; // 字符串a和b之间较大长度
	private char[] a; // 字符串a
	private char[] b; // 字符串b
	private char[] str; //最长公共子序列
	private int[][] c; // 当前最公共子序列的长度

	/**
	 * 构造方法
	 * 
	 * @param as
	 * @param bs
	 */
	public MaxLenPubSequence(String as, String bs) {
		a = as.toCharArray();
		b = bs.toCharArray();
		n = ((a.length > b.length) ? a.length : b.length);
		c = new int[a.length + 1][b.length + 1];
		str = new char[n];
	}
	
	/**
	 * 输出a和b最长公共子序列
	 */
	public void process() {
		int n = a.length;
		int m = b.length;
		int k = lcsLen(n, m);
		buileLcs(k, n, m);
		
		for (int i = 0; i < str.length; i++) {
			if (str[i] != '\0')
			System.out.print(str[i]);
		}
	}

	/**
	 * 计算最优值
	 * 
	 * @param i
	 * @param j
	 * @return
	 */
	private int lcsLen(int i, int j) {
		if (i == 0 || j == 0) {
			c[i][j] = 0;
		} else if (a[i - 1] == b[j - 1]) {
			c[i][j] = lcsLen(i - 1, j - 1) + 1;
		} else {
			int t1 = lcsLen(i, j - 1);
			int t2 = lcsLen(i - 1, j);
			c[i][j] = (t1 > t2 ? t1 : t2);
		}

		return c[i][j];
	}

	/**
	 * 构造最长公共子序列,k,i,j为字母序号
	 * 
	 * @param k
	 * @param i
	 * @param j
	 */
	private void buileLcs(int k, int i, int j) {
		if (i == 0 || j == 0) {
			return;
		}

		if (c[i][j] == c[i - 1][j]) {
			buileLcs(k, i - 1, j);
		} else if (c[i][j] == c[i][j - 1]) {
			buileLcs(k, i, j - 1);
		} else {
			str[k - 1] = a[i - 1];
			buileLcs(k - 1, i - 1, j - 1);
		}
	}

}

  
   (2) 动态规划非递归代码:

  
 package boke.written;

/**
 * 求两个字符串的最长公共字符子序列
 * 
 * @since jdk1.5及其以上
 * @author 毛正吉
 * @version 1.0
 * @date 2010.06.07
 * 
 */
public class MaxLenPubSequence2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String a = "ABCBDAB";
		String b = "BDCABA";
		MaxLenPubSequence2 mps2 = new MaxLenPubSequence2(a, b);
		mps2.lcsLen();
		mps2.buileLcs();

	}

	private int n; // 字符串a和b之间较大长度
	private char[] a; // 字符串a
	private char[] b; // 字符串b
	private char[] str; // 最长公共子序列
	private int[][] c; // 当前最公共子序列的长度

	/**
	 * 构造方法
	 * 
	 * @param as
	 * @param bs
	 */
	public MaxLenPubSequence2(String as, String bs) {
		a = as.toCharArray();
		b = bs.toCharArray();
		n = ((a.length > b.length) ? a.length : b.length);
		c = new int[a.length + 1][b.length + 1];
		str = new char[n];
	}

	/**
	 * 计算最优值
	 */
	public int lcsLen() {
		int n = a.length;
		int m = b.length;
		for (int i = 0; i <= n; i++) {
			c[i][0] = 0;
		}

		for (int j = 0; j <= m; j++) {
			c[0][j] = 0;
		}

		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (a[i - 1] == b[j - 1]) {
					c[i][j] = c[i - 1][j - 1] + 1;
				} else if (c[i - 1][j] > c[i][j - 1]) {
					c[i][j] = c[i - 1][j];
				} else {
					c[i][j] = c[i][j - 1];
				}
			}
		}
		return c[n][m];
	}

	/**
	 * 构造最长公共子序列,k,i,j为字母序号
	 * 
	 */
	private void buileLcs() {
		int k;
		int i = a.length;
		int j = b.length;
		
		k = lcsLen();
		while (k > 0) {
			if (c[i][j] == c[i - 1][j]) {
				i = i - 1;
			} else if (c[i][j] == c[i][j - 1]) {
				j = j - 1;
			} else {
				k--;
				str[k] = a[i - 1];
				j = j - 1;
			}
		}
		
		for (int l = 0; l < str.length; l++) {
			if (str[l] != '\0')
			System.out.print(str[l]);
		}
	}

}
分享到:
评论
2 楼 sun_left 2010-06-28  
楼主前面的一段话真的很诚恳,也希望je,甚至是国内coder之间也能有像楼主所说的那样的氛围.
1 楼 20055294 2010-06-28  
呵呵,楼主也是成都的哇,顶了,顺便学习

相关推荐

    动态规划求最长公共子序列

    在这个问题中,我们要找到两个字符串(序列)的最长公共子序列(Longest Common Subsequence, LCS)。最长公共子序列是指两个序列中存在的一段序列,它既出现在第一个序列中,也出现在第二个序列中,但不考虑元素的...

    “最长公共字符串子序列”问题的动态规划法算法.pdf

    `First_Born_SubStr_Len`函数是计算两个字符串的最长公共子序列长度的核心部分。这个函数首先初始化一个`substrlen`矩阵,其中`substrlen[i][j]`表示字符串`a`的前`i`个字符与字符串`b`的前`j`个字符之间的最长公共...

    最长公共子序列问题最长公共子序列问题

    在给定的两个序列X和Y中,目标是找到一个序列Z,它是X和Y的子序列,同时Z尽可能地长。这种子序列Z并不需要连续出现在原序列中,但它的每个元素都在X和Y中都有对应的位置。 问题描述明确指出,子序列是通过删除原...

    算法-最长公共子序列-LCS

    最长公共子序列(Longest Common Subsequence, LCS)是计算机科学中一种经典的问题,特别是在字符串处理、序列比对和算法设计领域有着广泛应用。LCS问题旨在寻找两个给定序列(通常是字符串)的最长子序列,这个子...

    最长公共子序列

    在给定的代码中,作者实现了一个基于动态规划的解决方案来求解两个字符串的最长公共子序列。动态规划是一种通过将大问题分解为子问题来解决的方法,这里的关键是构建一个二维数组`c`来存储中间结果。数组`c`的每个...

    python实现最长公共子序列

    通过运行这个程序,我们可以得到两个字符串`a`和`b`的最长公共子序列以及它们的长度。在这个例子中,字符串`a='ABCBDAB'`和`b='BDCABA'`的LCS是`BCBA`,长度为4。 总结来说,Python实现最长公共子序列的关键在于...

    最大公共子序列,实现公共子序列算法 with c sharp

    假设我们有两个字符串X和Y,我们的目标是找到它们的最长公共子序列。一个简单的递归解决方案可以描述为: 1. 如果X或Y为空,则LCS长度为0。 2. 如果X的最后一个字符与Y的最后一个字符相同,那么LCS的长度就是前两个...

    动态规划 源码

    2. **最长公共子序列**:两个字符串的最长公共子序列问题,可以通过动态规划构建一个二维矩阵来求解,每一行每一列的值表示对应前缀的最长公共子序列长度。 3. **背包问题**:0-1背包问题和完全背包问题,是动态...

    动态规划算法学习十例之三

    2. **最长公共子序列(LCS)**:在两个字符串中找到最长的没有重新排列的子序列。这个问题可以通过构建二维数组,记录两个字符串中对应位置的子序列长度来解决。 3. **背包问题**:在给定的容量限制下,选择物品以...

    南京邮电大学 算法设计与分析 陈慧南 实验二动态规划法实验报告

    在南京邮电大学的算法设计与分析课程中,动态规划法是解决最优化问题的重要算法策略之一,而实验二动态规划法实验报告主要涉及了动态规划在求解最长公共子序列(LCS)问题和矩阵连乘问题中的应用。以下是对这部分...

    算法设计与分析之动态规划

    4. **最小编辑距离**:计算两个字符串之间的转换最少操作次数(插入、删除、替换),可以使用动态规划来解决,矩阵中的每个元素表示达到对应位置所需的最小操作数。 5. **图的最小割**:在图论中,寻找一个分割图的...

    lcs.rar_LCS_动态规划

    对于LCS问题,我们可以构建一个二维数组dp,其中dp[i][j]表示输入序列s1的前i个字符和s2的前j个字符的最长公共子序列的长度。 首先,我们需要初始化dp数组的边界条件。如果其中一个序列为空,那么最长公共子序列的...

    多阶段决策过程问题的动态规划算法

    例如,最长公共子序列问题可以通过比较两个子序列的公共部分来找到。 3. 自底向上的计算:从最小规模的子问题开始,逐步计算更大规模问题的最优值,存储这些值以供后续使用。这一步通常通过填充一个表格(也称为...

    zju动态规划试题选集

    - **最长公共子序列**:寻找两个字符串的最长子序列,不考虑子序列的顺序,可以使用动态规划的二维数组进行求解。 - **斐波那契数列**:典型的递归与动态规划问题,动态规划能有效避免指数级的递归计算。 - **最短...

    动态规划算法及其应用.pdf

    动态规划可以用来解决此问题,通过构造一个二维数组来记录两个序列在当前位置之前所能达到的最长公共子序列的长度。实验中没有提供具体的LCS问题的代码,但通常会有一个类似的过程,比较两个序列的当前字符是否相等...

    LCSwork工作

    这部分代码定义了一个名为`LCSwork`的类,该类中包含了两个成员函数:`LCSLength` 和 `LCS`,分别用于计算两个字符串的最长公共子序列长度及其具体子序列。 - **`LCSLength` 函数**: - 输入参数: - `m` 和 `n` ...

    动态规划算法案例

    状态可以定义为dp[i][j],表示序列1的前i个字符和序列2的前j个字符的最长公共子序列的长度。状态转移方程为dp[i][j] = max(dp[i-1][j-1] + (if A[i] == B[j]), dp[i-1][j], dp[i][j-1]),其中A[i]和B[j]分别代表两个...

    动态规划经典问题 刘汝佳 pdf格式

    **定义**: 最长公共子序列(Longest Common Subsequence, LCS)问题是寻找两个序列中最长的公共子序列。这里的子序列指的是从原序列中删除某些元素后剩余元素保持原顺序所形成的序列。 **状态定义**: 定义`c[i,j]`为...

    南京邮电大学算法分析与设计实验二(动态规划法)

    2. **构造函数**:在构造函数中初始化了两个输入字符串`x`和`y`以及它们的长度`m`和`n`,并为动态规划所需的二维数组`c`和`s`分配内存。 3. **析构函数**:析构函数负责释放之前分配的内存资源。 4. **动态规划求解*...

Global site tag (gtag.js) - Google Analytics