注:

  • 最长公共子序列采用动态规划解决,由于子问题重叠,故采用数组缓存结果,保存最佳取值方向。输出结果时,则自顶向下建立二叉树,自底向上输出,则这过程中没有分叉路,结果唯一。
  • 最长公共子串采用参考串方式,逐步比对,若相同则对应参考值自增,同时记录当前时刻最大参考值,及其位置。最后输出多组结果。

源码:lcs.cpp

#include "stdafx.h"
#include <stdio.h>
#include <vector> /************************************************************************/
/* 最长公共子序列 / 最长公共子串 */
/* lcseq / lcstr */
/************************************************************************/ //注意!需要输出多组结果! int length(char *pstr)
{
int i;
i = 0;
while (*pstr++) i++;
return i;
} void print_matrix(int **mat, char *x, char *y, int m0, int n0, int m, int n)
{
int i, j;
printf(" ");
for (j = 0; j < n; j++)
{
printf("%-3c", y[j]);
}
printf("\n");
for (i = m0; i < m; i++)
{
printf("%-4c", x[i - m0]);
for (j = n0; j < n; j++)
{
printf("%-3d", mat[i][j]);
}
printf("\n");
}
} /*
* > 子串是串的一个连续的部分
* > 子序列则是不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列
* > 【也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。】
*/ struct node
{
int parent;
char c;
}; //************************************
// Method: 构建二叉树
// FullName: build_btree
// Access: public
// Returns: void
// Qualifier:
// Parameter: char * x 原始字符串X
// Parameter: int * * mat 跟踪矩阵
// Parameter: int m 串X长度
// Parameter: int n 串Y长度
// Parameter: int parent 父结点ID
// Parameter: std::stack<node> & tree 树
//************************************
void build_btree(char *x, int **mat, int m, int n, int parent, std::vector<node>& tree)
{
if (m == -1 || n == -1)//跟踪到叶子结点,可以打印
{
int i;
for (i = parent; i != -1; i = tree[i].parent)
{
char c;
c = tree[i].c;
if (c)
{
printf("%c", c);
}
}
printf("\n");
return;
}
if (m == 0 && n == 0)//处理左上角结点的情况Z(0,0)
{
build_btree(x, mat, -1, -1, parent, tree);
return;
}
int p = mat[m][n];
if (mat[m][n] == 0)//防止访问重复结点
return;
mat[m][n] = 0;
node newnode;
newnode.c = '\0';
newnode.parent = parent;
tree.push_back(newnode);
parent = tree.size() - 1;
switch (p)
{
case 1://左上,相同
tree.back().c = x[m];
build_btree(x, mat, m, n - 1, parent, tree);
break;
case 2://上
build_btree(x, mat, m - 1, n, parent, tree);
break;
case 3://左
build_btree(x, mat, m, n - 1, parent, tree);
break;
case 4://左和上
build_btree(x, mat, m, n - 1, parent, tree);
build_btree(x, mat, m - 1, n, parent, tree);
break;
default:
break;
}
} //************************************
// Method: 最长公共子序列(DP动态规划)
// FullName: lcseq
// Access: public
// Returns: void
// Qualifier:
// Parameter: char * x
// Parameter: char * y
//************************************
void lcseq(char *x, char *y)
{
//串x:X[ 0 .. m-1 ] x.length=m
//串y:Y[ 0 .. n-1 ] y.length=n
//串z:Z[ 0 .. k-1 ] z.length=k, z = lcseq(x,y) //【z是x与y的最长公共子序列】
//
//================ 递推方法 ================
//从最后一位看起:
//
// >>> 第一种情况:
// * 若X[last]==Y[last],则z[last]=X[last]=Y[last]
// * 则lcseq(x,y) = lcseq(x-1,y-1) + (x[last] or y[last])
//
// >>> 第二种情况:
// * 若X[last]<>Y[last],考虑:
// * 1) 若Z[last]==X[last],则lcseq(x,y) = lcseq(x,y-1)
// * 2) 若Z[last]==Y[last],则lcseq(x,y) = lcseq(x-1,y)
// * 3) 否则,lcseq(x,y) = max_length{lcseq(x,y-1), lcseq(x-1,y)}
// * 综合得lcseq(x,y) = max_length{lcseq(x,y-1), lcseq(x-1,y)}
//
//图表展示:
// ┏━━━━━━━┳━━━━━━━┓
// ┃ Z(i-1,j-1) ┃ Z(i-1,j) ┃
// ┣━━━━━━━╋━━━━━━━┫
// ┃ Z(i,j-1) ┃ Z(i,j) ┃
// ┗━━━━━━━┻━━━━━━━┛
//
//================ 总结递推公式 ================
// 【已知Z(i-1,j-1)、Z(i-1,j)、Z(i,j-1),求Z(i,j)】
// 初始:
// 1) Z(0,0)=0
// 2) Z(0,~)=0
// 3) Z(~,0)=0
// 递推:
// 1) X[i] == Y[j] ==> Z(i,j) = Z(i-1,j-1) + 1
// 2) X[i] <> Y[j] ==> Z(i,j) = max{Z(x,y-1), Z(x-1,y)}
//
//================ 跟踪输出所有LCS ================
//由Z(m,n)可以向左上方遍历,所过所有路径即形成二叉树
//故考虑建立二叉树,实行自叶子向根部的遍历 int m, n, i, j;
int **zmat, *zmat0;
int **pmat, *pmat0;
std::vector<node> tree;//保存二叉树的数组(栈式存储)
//计算长度
m = length(x);
n = length(y);
//建立保存Z(i,j)函数值的二维数组,定义域为{(i,j)|i in [0..m] and j in [0..n]}
zmat = new int*[m + 1];//指针数组
zmat0 = new int[(m + 1) * (n + 1)];//m+1行n+1列,一维数组建立
for (i = 0; i <= m; i++) zmat[i] = &zmat0[i * (n+1)];
for (i = 0; i <= m; i++) zmat[i][0] = 0;//i行0列置0
for (j = 1; j <= n; j++) zmat[0][j] = 0;//0行j列置0
//建立跟踪数组,跟踪Z(i,j)的取值方向(第一种情况为左上角取值,第二种情况为上边或左边取值)
pmat = new int*[m];//指针数组
pmat0 = new int[m * n];//m行n列,一维数组建立
for (i = 0; i < m; i++) pmat[i] = &pmat0[i * n];
//填充剩余的Z数组
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)//注意i,j范围
{
if (x[i - 1] == y[j - 1])//X[i] == Y[j] ==> Z(i,j) = Z(i-1,j-1) + 1
{
zmat[i][j] = zmat[i - 1][j - 1] + 1;
pmat[i - 1][j - 1] = 1;//左上角取值路线
}
else//X[i] <> Y[j] ==> Z(i,j) = max{Z(x,y-1), Z(x-1,y)}
{
int sub;
sub = zmat[i - 1][j] - zmat[i][j - 1];
if (sub >= 0)
{
zmat[i][j] = zmat[i - 1][j];
pmat[i - 1][j - 1] = sub == 0 ? 4 : 2;//左或上取值路线
}
else
{
zmat[i][j] = zmat[i][j - 1];
pmat[i - 1][j - 1] = 3;//左边取值路线
}
}
}
}
printf("Z matrix\n");
print_matrix(zmat, x, y, 1, 1, m + 1, n + 1);
printf("Trace matrix\n");
print_matrix(pmat, x, y, 0, 0, m, n); //跟踪开始
node root;
root.parent = -1;
root.c = '\0';
tree.push_back(root);
printf("------------- Result -------------\n");
build_btree(x, pmat, m - 1, n - 1, 0, tree);
//清理工作
delete[] zmat;
delete[] zmat0;
delete[] pmat;
delete[] pmat0;
} //************************************
// Method: 最长公共子串(DP动态规划)
// FullName: lcstr
// Access: public
// Returns: void
// Qualifier:
// Parameter: char * x
// Parameter: char * y
//************************************
void lcstr(char *x, char *y)
{
//以Y作参考串,建立参考数组Z,然后遍历X
//即:对所有i,存在j,使X[i]==Y[j],则Z'[j]=Z[j]+1
//跟踪Z中最大值
int i, j, m, n, *z, max;
std::vector<int> str_indexes;//记录符合要求的lcstr起始位置(可能有多个)
m = length(x);
n = length(y);//Y作参考串
z = new int[n + 1];
for (j = 0; j <= n; j++) z[j] = 0;//初始化
max = 0;
printf(" ");
for (j = 0; j < n; j++)
{
printf("%-3c", y[j]);
}
printf("\n");
for (i = 0; i < m; i++)
{
char c;
c = x[i];
printf("%-4c", c);
for (j = n - 1; j >= 0; j--)
{
if (c == y[j])
{
z[j + 1] = z[j] + 1;
int zj = z[j + 1];
if (zj > max)
{
max = zj;
str_indexes.clear();
}
if (zj == max)
{
str_indexes.push_back(j - zj);
}
}
else
{
z[j + 1] = 0;
}
}
for (j = 1; j <= n; j++)
{
printf("%-3d", z[j]);
}
printf("\n");
}
printf("------------- Result -------------\n");
for (auto& v : str_indexes)
{
for (j = v; j < max + v; j++)
{
printf("%c", y[j]);
}
printf("\n");
}
delete[] z;
} int main(int argc, char* argv[])
{
char *str1 = "BADCDCBA";
char *str2 = "ABCDCDAB";
printf("a: %s\n", str1);
printf("b: %s\n", str2);
printf("========= lcseq ==========\n");
lcseq(str1, str2);
printf("\n");
printf("========= lcstr ==========\n");
lcstr(str1, str2);
printf("\n");
return 0;
}

动态规划(一)——最长公共子序列和最长公共子串的更多相关文章

  1. 动态规划 最长公共子序列 LCS,最长单独递增子序列,最长公共子串

    LCS:给出两个序列S1和S2,求出的这两个序列的最大公共部分S3就是就是S1和S2的最长公共子序列了.公共部分 必须是以相同的顺序出现,但是不必要是连续的. 选出最长公共子序列.对于长度为n的序列, ...

  2. 动态规划1——最长递增子序列、最长公共子序列、最长公共子串(python实现)

    目录 1. 最长递增序列 2. 最长公共子序列 3. 最长公共子串 1. 最长递增序列 给定一个序列,找出其中最长的,严格递增的子序列的长度(不要求连续). 解法一:动态规划 通过一个辅助数组记录每一 ...

  3. [Python]最长公共子序列 VS 最长公共子串[动态规划]

    前言 由于原微软开源的基于古老的perl语言的Rouge依赖环境实在难以搭建,遂跟着Rouge论文的描述自行实现. Rouge存在N.L.S.W.SU等几大子评估指标.在复现Rouge-L的函数时,便 ...

  4. [Data Structure] LCSs——最长公共子序列和最长公共子串

    1. 什么是 LCSs? 什么是 LCSs? 好多博友看到这几个字母可能比较困惑,因为这是我自己对两个常见问题的统称,它们分别为最长公共子序列问题(Longest-Common-Subsequence ...

  5. 最长公共子序列与最长公共字串 (dp)转载http://blog.csdn.net/u012102306/article/details/53184446

    1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...

  6. 用Python计算最长公共子序列和最长公共子串

    如何用Python计算最长公共子序列和最长公共子串 1. 什么是最长公共子序列?什么是最长公共子串? 1.1. 最长公共子序列(Longest-Common-Subsequences,LCS) 最长公 ...

  7. O(n log n)求最长上升子序列与最长不下降子序列

    考虑dp(i)表示新上升子序列第i位数值的最小值.由于dp数组是单调的,所以对于每一个数,我们可以二分出它在dp数组中的位置,然后更新就可以了,最终的答案就是dp数组中第一个出现正无穷的位置. 代码非 ...

  8. 最长公共子序列PK最长公共子串

    1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的.而最长公共子序列则并不要求连续. (1)递归方法求最长公共子序列的长度 1) ...

  9. 【ZH奶酪】如何用Python计算最长公共子序列和最长公共子串

    1. 什么是最长公共子序列?什么是最长公共子串? 1.1. 最长公共子序列(Longest-Common-Subsequences,LCS) 最长公共子序列(Longest-Common-Subseq ...

随机推荐

  1. 时光煮雨 Unity3D实现2D人物动画① UGUI&Native2D序列帧动画

    系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoT ...

  2. curl常用选项详解

    curl常用选项详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 又是下班的时间了,让我们一起来学习一下今天的Linux命令吧~我一半只把自己常用的参数列出来,其他的有但是我们几 ...

  3. csuoj 1114: 平方根大搜索

    http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1114 1114: 平方根大搜索 Time Limit: 5 Sec  Memory Limit:  ...

  4. Pyhton 学习总结 20 :执行系统命令

    在Python中执行系统命令有os.system().os.popen().commands.getstatusoutput().subprocess.Popen等     1.os.system() ...

  5. php递归无限极分类实例

    无限级分类原理简介 无限分类看似"高大上",实际上原理是非常简单的 .无限分类不仅仅需要代码的巧妙性,也要依托数据库设计的合理性.要满足无限级分类,数据库需要有两个必须的字段,id ...

  6. 深入理解Java PriorityQueue

    PriorityQueue 本文github地址 Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析Prio ...

  7. 05-IP核应用之计数器——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线--普利斯顿队长精心奉献   实验目的:了解FPGA的IP核相关知识并以计数器IP核为例学会基本IP使用的流程 实验平台:无 实验原理:     IP核(Intellectual Propert ...

  8. MVC5 + EF6 完整入门教程三

    期待已久的EF终于来了. 学完本篇文章,你将会掌握基于EF数据模型的完整开发流程. 本次将会完成EF数据模型的搭建和使用. 基于这个模型,将之前的示例添加数据库查询验证功能. 文章提纲 概述 & ...

  9. datagridview随窗体的大小而变,表格填满控件

    在C#winform布局的时候,我们拖一个datagridview到窗体上面,将datagridview调整为适合窗体的大小,但是我们运行之后,点击最大化按钮的时候,却发现datagridview的大 ...

  10. llinux 压缩 解压

    1.zip  1) 将文件夹 mydir 压缩为 mydir.zip zip -r mydir.zip mydir 2) 将文件 one.two 压缩到 ot.zip zip -r ot.zip on ...