当前位置: 首页 > news >正文

【动态规划】最长公共子序列问题 C++

问题描述

  • 子序列:序列Z是原序列X的子序列,当且仅当Z的元素在X中按严格递增的下标顺序出现(不要求连续)。例如X={A,B,C,B,D,A,B}中,Z={B,C,D,B}是子序列,对应X的下标2→3→5→7。
  • 公共子序列:若序列Z同时是X和Y的子序列,则称Z为X和Y的公共子序列

最长公共子序列问题要求给定两个序列X、Y,找出X、Y的最长公共子序列

问题分析

  1. 最长公共子序列的结构
    设序列X={x1,x2,......,xm}Y={y1,y2,......,yn}的最长公共子序列为Z={z1,z2,......,zk},则:
    (1)若xm=yn,则zk=xm=yn,且Z-zkX-xmY-yn的最长公共子序列
    (2)若xm≠yn且zk≠xm,则ZX-xmY的最长公共子序列
    (3)若xm≠yn且zk≠yn,则ZXY-yn的最长公共子序列

由此可见,2个序列的最长公共子序列包含了这两个序列的前缀最长公共子序列。因此,最长公共子序列问题具有最优子结构性质。

  1. 建立最优值的递归方程
    c [ i ] [ j ] = { 0 i = 0   o r j = 0 c [ i − 1 ] [ j − 1 ] + 1 i , j > 0 ;   x i = y j max ⁡ { c [ i ] [ j − 1 ] , c [ i − 1 ] [ j ] } i , j > 0 ;   x i ≠ y j c[i][j] = \begin{cases} 0 & i = 0 \ or j = 0 \\ c[i-1][j-1] + 1 & i, j > 0;\ x_i = y_j \\ \max\{c[i][j-1], c[i-1][j]\} & i, j > 0;\ x_i \neq y_j \end{cases} c[i][j]= 0c[i1][j1]+1max{c[i][j1],c[i1][j]}i=0 orj=0i,j>0; xi=yji,j>0; xi=yj
  2. 自底向上求最优值
    在子问题空间中,共有mn个不同的子问题

遍历整个子问题空间,计算并存储当前问题的解

void LcsLength(char x[], char y[], int m, int n,int **c, int **b) {
	for (int i = 0; i <= m; i++)
		c[i][0] = 0;
	for (int j = 0; j <= n; j++)
		c[0][j] = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			if (x[i] == y[j])
			{
				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];
			}

		}
	}
}
  1. 构造最优解

在上一步求最优值的时候,加入辅助信息表b,用于存储当前位置的值的来源
我们规定,
b[i][j]=1,表示c[i][j]=c[i-1][j-1]+1
b[i][j]=2,表示c[i][j]=c[i-1][j]
b[i][j]=3,表示c[i][j]=c[i][j-1]

加入辅助信息的LcsLength函数

void LcsLength(char x[], char y[], int m, int n,int **c, int **b) {
	for (int i = 0; i <= m; i++)
		c[i][0] = 0;
	for (int j = 0; j <= n; j++)
		c[0][j] = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; 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] = 2;
			}
			else
			{
				c[i][j] = c[i][j - 1];
				b[i][j] = 3;
			}

		}
	}
}

构造最优解

void LCS(int **b, int i,int j,char x[]) {
	if (i == 0 || j == 0)
		return;
	if (b[i][j] == 1)
	{
		LCS(b, i - 1, j - 1,x);
		cout << x[i];
	}
	else if (b[i][j] == 2)
		LCS(b, i - 1, j, x);
	else
		LCS(b, i, j - 1, x);
}

实例

设两个序列
X={a,b,c,b,d,a,b}
Y={b,d,c,a,b,a}

可计算出如下的c[i][j]b[i][j]
在这里插入图片描述
在这里插入图片描述

代码

#include<iostream>
using namespace std;
void LCS(int b[][8], int i,int j,char x[]) {
	if (i == 0 || j == 0)
		return;
	if (b[i][j] == 1)
	{
		LCS(b, i - 1, j - 1,x);
		cout << x[i];
	}
	else if (b[i][j] == 2)
		LCS(b, i - 1, j, x);
	else
		LCS(b, i, j - 1, x);
}
void LcsLength(char x[], char y[], int m, int n,int c[][8], int b[][8]) {
	for (int i = 0; i <= m; i++)
		c[i][0] = 0;
	for (int j = 0; j <= n; j++)
		c[0][j] = 0;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; 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] = 2;
			}
			else
			{
				c[i][j] = c[i][j - 1];
				b[i][j] = 3;
			}

		}
	}
}
int main() {
	char x[8] = { ' ','a','b','c','b','d','a','b' };
	char y[7] = { ' ','b','d','c','a','b','a' };
	int c[8][8];
	int b[8][8];
	int m = 7, n = 6;
	LcsLength(x, y, m, n, c, b);
	cout << "最长公共子序列长度为" << c[m][n] << endl;
	cout << "最长公共子序列为:";
	LCS(b, m, n, x);

	return 0;
}

运行结果
在这里插入图片描述

在这个问题中,最长公共子序列有多种情况,由于我们的规定,找到了bdab这种情况,如果更改我们的规定,令c[i - 1][j] > c[i][j - 1]时,c[i][j]=3,则会找到另一种子序列

优化
我们可以注意到,c数组中每个位置的值只于它左侧、上方、左上方这三个值有关,可以优化掉多余的空间开销;同样,b数组也可以优化掉,我们只用c数组就能确定当前值c[i][j]来自c[i-1][j-1]、c[i][j-1]、c[i-1][j]中的哪一个

相关文章:

  • 深入理解与使用 HashedWheelTimer:高效的时间轮定时器
  • python argparse 参数使用详解记录
  • Qt 制作验证码
  • SQL语句---特殊查询
  • 递归,搜索,回溯算法(一)
  • 多版本PHP开发环境配置教程:WAMPServer下MySQL/Apache/MariaDB版本安装与切换
  • ubuntu下docker 安装 graylog 6.1
  • HTML输出流
  • WebMvcConfigurer 的 addResourceLocations
  • Eplan许可管理的自动化工具
  • [Vue2]指令修饰符(一)
  • [问题收集]mysql主从分离过程中,数据不同步可能导致的后果以及应对策略
  • NFC 智能门锁全栈解决方案:移动端、服务器、Web 管理平台
  • src案例分享-逻辑漏洞
  • 软路由用联想j3710主板踩坑
  • 从0到1,解锁Ant Design X的无限可能
  • 能源革命新突破:虚拟电厂赋能微电网智能调控,构建低碳生态新格局
  • MCP插件使用(browser-tools-mcp为例)
  • 通过一个led点灯的demo来熟悉openharmony驱动编写的过程(附带hdf详细调用过程)
  • 【windows搭建lvgl模拟环境之VSCode】
  • 花20万骑自行车?CityRide带火“骑行经济”
  • 金正恩视察重要军工企业要求推进武力强化变革
  • 魔都眼|上海多家商场打开绿色通道,助力外贸出口商品转内销
  • 指挥家高健:东方市民音乐会“高贵不贵”,我愿意常来
  • 多省份晒出“五一”旅游“成绩单”:北京游客接待量、旅游消费创历史新高
  • 新华每日电讯:上海“绿色大民生”撑起“春日大经济”