经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。
为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法。
【问题】 求两字符序列的最长公共字符子序列
问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-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,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
求解:
引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
问题的递归式写成:
回溯输出最长公共子序列过程:
算法分析:
由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m * n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m * n)。
Java代码实现:
- public class LCSProblem
- {
- public static void main(String[] args)
- {
- //保留空字符串是为了getLength()方法的完整性也可以不保留
- //但是在getLength()方法里面必须额外的初始化c[][]第一个行第一列
- String[] x = {"", "A", "B", "C", "B", "D", "A", "B"};
- String[] y = {"", "B", "D", "C", "A", "B", "A"};
- int[][] b = getLength(x, y);
- Display(b, x, x.length-1, y.length-1);
- }
- /**
- * @param x
- * @param y
- * @return 返回一个记录决定搜索的方向的数组
- */
- public static int[][] getLength(String[] x, String[] y)
- {
- int[][] b = new int[x.length][y.length];
- int[][] c = new int[x.length][y.length];
- for(int i=1; i<x.length; i++)
- {
- for(int j=1; j<y.length; j++)
- {
- //对应第一个性质
- if( x[i] == y[j])
- {
- c[i][j] = c[i-1][j-1] + 1;
- b[i][j] = 1;
- }
- //对应第二或者第三个性质
- else if(c[i-1][j] >= c[i][j-1])
- {
- c[i][j] = c[i-1][j];
- b[i][j] = 0;
- }
- //对应第二或者第三个性质
- else
- {
- c[i][j] = c[i][j-1];
- b[i][j] = -1;
- }
- }
- }
- return b;
- }
- //回溯的基本实现,采取递归的方式
- public static void Display(int[][] b, String[] x, int i, int j)
- {
- if(i == 0 || j == 0)
- return;
- if(b[i][j] == 1)
- {
- Display(b, x, i-1, j-1);
- System.out.print(x[i] + " ");
- }
- else if(b[i][j] == 0)
- {
- Display(b, x, i-1, j);
- }
- else if(b[i][j] == -1)
- {
- Display(b, x, i, j-1);
- }
- }
- }
最长公共子字符串:类似最长子序列,只是公共子字符串要求必须是连续的。
java实现代码如下:
- public class stringCompare {
- //在动态规划矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了,因此这里只需使用一维数组,而不是常用的二位数组
- public static void getLCString(char[] str1, char[] str2) {
- int len1, len2;
- len1 = str1.length;
- len2 = str2.length;
- int maxLen = len1 > len2 ? len1 : len2;
- int[] max = new int[maxLen];// 保存最长子串长度的数组
- int[] maxIndex = new int[maxLen];// 保存最长子串长度最大索引的数组
- int[] c = new int[maxLen];
- int i, j;
- for (i = 0; i < len2; i++) {
- for (j = len1 - 1; j >= 0; j--) {
- if (str2[i] == str1[j]) {
- if ((i == 0) || (j == 0))
- c[j] = 1;
- else
- c[j] = c[j - 1] + 1;//此时C[j-1]还是上次循环中的值,因为还没被重新赋值
- } else {
- c[j] = 0;
- }
- // 如果是大于那暂时只有一个是最长的,而且要把后面的清0;
- if (c[j] > max[0]) {
- max[0] = c[j];
- maxIndex[0] = j;
- for (int k = 1; k < maxLen; k++) {
- max[k] = 0;
- maxIndex[k] = 0;
- }
- }
- // 有多个是相同长度的子串
- else if (c[j] == max[0]) {
- for (int k = 1; k < maxLen; k++) {
- if (max[k] == 0) {
- max[k] = c[j];
- maxIndex[k] = j;
- break; // 在后面加一个就要退出循环了
- }
- }
- }
- }
- for (int temp : c) {
- System.out.print(temp);
- }
- System.out.println();
- }
- //打印最长子字符串
- for (j = 0; j < maxLen; j++) {
- if (max[j] > 0) {
- System.out.println("第" + (j + 1) + "个公共子串:");
- for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++)
- System.out.print(str1[i]);
- System.out.println(" ");
- }
- }
- }
- public static void main(String[] args) {
- String str1 = new String("binghaven");
- String str2 = new String("jingseven");
- getLCString(str1.toCharArray(), str2.toCharArray());
- }
- }
输出:
000000000
010000000
002000001
000300000
000000000
000000010
000000100
000000020
001000003
第1个公共子串:
ing
第2个公共子串:
ven
相关推荐
主要介绍了Java基于动态规划法实现求最长公共子序列及最长公共子字符串,简单描述了动态规划法的概念、原理,并结合实例形式分析了Java使用动态规划法求最长公共子序列以及最长公共子字符串相关实现技巧,需要的朋友...
自己编写的C++程序,求两个字符串的最长公共字串。例如a="abcrrrerads",b="afdabcssbcrrresswrds",则结果为bcrrre
最长公共子序列(Longest Common Subsequence, LCS)问题是计算机科学和...它要求找出两个序列(如字符串、列表或数组)的最长公共子序列,即在两个序列中以相同顺序出现,并且不改变原有序列中元素顺序的最长子序列。
当前实现了十二种算法(包括Levenshtein编辑距离和同级,Jaro-Winkler,最长公共子序列,余弦相似性等)。 查看下面的摘要表以获取完整列表... 下载 使用Maven: <groupId>info.debatty <artifactId>java-...
濒海战斗舰 LCS 类比较两个文本文件并找到最长公共子串 (LCS)...此代码用于通过命令行比较两个文本文件并返回两者共享的最长公共子字符串。 对于这个项目,除了另外两本达特茅斯文本之外,还使用了白鲸记和战争与和平。
java-string-similarity, 各种字符串相似性和距离算法 java-string-similarity 实现不同字符串相似度和距离... 目前已经实现了许多算法( 包括Levenshtein编辑距离和 sibblings,jaro winkler,最长公共子序列,余弦相
各种字符串相似度和距离算法的实现:Levenshtein,Jaro-winkler,n-Gram,Q-Gram,Jaccard索引,最长公共子序列编辑距离,余弦相似度......
算法该项目试图计算归一化的编辑距离和两个序列之间最长的公共子序列编译:输入文件将通过控制台作为主函数的参数提供。 如果文件不在父目录/工作目录中,则需要指定整个文件路径。 如果指定了整个路径,则很容易...
当前实现了十二种算法(包括Levenshtein编辑距离和同级,Jaro-Winkler,最长公共子序列,余弦相似性等)。 查看下面的摘要表以获取完整列表...下载使用NuGet: Install-Package F23.StringSimilarity总览下面介绍了...
Algorithm-java-string-similarity.zip,各种字符串相似度和距离算法的实现:levenshtein、jaro winkler、n-gram、q-gram、jaccard索引、最长公共子序列编辑距离、余弦相似度……,算法是为计算机程序高效、彻底地完成...
leetcode数组下标大于间距 ...输入三个字符串s1、s2和s3,判断第三个字符串s3是否由前两个字符串s1和s2交错而成, 即不改变s1和s2中各个字符原有的相对顺序,例如当s1 = “aabcc”,s2 = “dbbca”, s3 =
动态规划解决最长公共子序列问题 背包问题 最小堆 并查集 最短路径 Dijkstra 算法 字符串匹配 KMP 算法 最小生成树 Kruskal 算法与 Prim 算法 八大排序算法 Java 一开始均使用 Java 语言实现,后续复习打算再使用 ...
目录 第一章 从零开始 8 1.1机试分析 8 1.2 IDE的选择与评测结果 10 1.3 DreamJudge的使用 11 ...8.4 最长公共子序列(LCS) 174 8.5 背包类问题 176 8.6 记忆化搜索 179 8.7 字符串相关的动态规划 182
leetcode 蓄水池JAVA 编程面试 学习资源 基本 复杂 数据结构 解决方案模式 问题模式 ...最长公共子序列 LeetCode-300 最长递增子序列 LeetCode-5 最长回文子串 LeetCode-298 二叉树最长连续序列 LeetCode-
最长公共子序列 最大子集总和 排序 Timsort 基数排序 计数排序 快速排序 堆排序 合并排序 插入排序 气泡排序 选择排序 Bogosort 正在搜寻 线性的 二元 订单统计 排序选择 随机选择 堆选择 快速选择 弦乐 字符串搜索...
最长公共前缀 最小窗口子串, 有效回文, 反向字符串, 反向字符串 II , 反向字符串 III , 反转元音 , 删除元音, 查找常见字符, 最常用的词, 长按名称, 数组 二和, 3总和更小, 排序数组中的单个元素, 第三个...
最大和递增子序列 数组中的领先者 最小平台 大小为 k 的所有子数组的最大值 组中的反向数组 第 K 个最小元素 捕集雨水勾股三重巧克力分布问题股票买卖左侧较小右侧较大的元素将数组转换为Zig-Zag方式查找已排序数组...
java lru leetcode LeetCode-Java ...最长公共子序列 最长回文子串 宝藏岛 克隆图 难的 拓扑排序 最长连续序列 设计问题 设计哈希图 设计链表(Linked-List Conctruction) 设计井字游戏 设计搜索自动完成系统
多米诺骨牌算法leetcode ...最长递增子序列 硬币变化 最大化切割段 使分区相等的子集的最小总和分区 多米诺骨牌放置 逆因子 在旋转列表中找到最大的数字 可整除数 leetcode 332 硬币找零问题 最大积子
算法- Dijkstra的最短路径算法(Python)Kahn的拓扑排序算法深度优先搜索| | Floyd Warshall算法最长公共子序列(Java) 最长的公共子字符串(Java) 数组旋转反转方法模式搜索KMP(Knuth Morris Pratt)算法数据...