最大公共子序列(动规)

问题描述:一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X= { x1, x2,…, xm},则另一序列Z= {z1, z2,…, zk}是X的子序列是指存在一个严格递增的下标序列 {i1, i2,…, ik},使得对于所有j=1,2,…,k有 Xij=Zj。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X= { A, B, C, B, D, A, B}和Y= {B, D, C, A, B, A},则序列{B,C,A}是X和Y的一个公共子序列,序列{B,C,B,A}也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。给定两个序列X= {x1, x2, …, xm}和Y= {y1, y2, … , yn},要求找出X和Y的一个最长公共子序列。

问题解析:设X= { A, B, C, B, D, A, B},Y= {B, D, C, A, B, A}。求X,Y的最长公共子序列最容易想到的方法是穷举法。对X的多有子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列。由集合的性质知,元素为m的集合共有2^m个不同子序列,因此,穷举法需要指数级别的运算时间。进一步分解问题特性,最长公共子序列问题实际上具有最优子结构性质。

设序列X={x1,x2,……xm}和Y={y1,y2,……yn}的最长公共子序列为Z={z1,z2,……zk}。则有:

(1)若xm=yn,则zk=xm=yn,且zk-1是Xm-1和Yn-1的最长公共子序列。

(2)若xm!=yn且zk!=xm,则Z是Xm-1和Y的最长公共子序列。

(3)若xm!=yn且zk!=yn,则Z是X和Yn-1的最长公共子序列。

其中,Xm-1={x1,x2……xm-1},Yn-1={y1,y2……yn-1},Zk-1={z1,z2……zk-1}。

递推关系:用c[i][j]记录序列Xi和Yj的最长公共子序列的长度。其中,Xi={x1,x2……xi},Yj={y1,y2……yj}。当i=0或j=0时,空序列是xi和yj的最长公共子序列。此时,c[i][j]=0;当i,j>0,xi=yj时,c[i][j]=c[i-1][j-1]+1;当i,j>0,xi!=yj时,

c[i][j]=max{c[i][j-1],c[i-1][j]},由此建立递推关系如下:

#include <iostream>
using namespace std;

const int M = 7;
const int N = 6;

void output(char *s, int n);
void LCSLength(int m, int n, char *x, char *y, int **c);
void LCS(int i, int j, char *x, int **c);

int main()
{
//X={A,B,C,B,D,A,B}
//Y={B,D,C,A,B,A}
char x[] = { ‘ ‘,‘A‘,‘B‘,‘C‘,‘B‘,‘D‘,‘A‘,‘B‘ };
char y[] = { ‘ ‘,‘B‘,‘D‘,‘C‘,‘A‘,‘B‘,‘A‘ };

int **c = new int *[M + 1];
for (int i = 0; i <= M; i++)
{
c[i] = new int[N + 1];
}

cout << "序列X:" << endl;
output(x, M);
cout << "序列Y:" << endl;
output(y, N);

LCSLength(M, N, x, y, c);

cout << "序列X、Y最长公共子序列长度为:" << c[M][N] << endl;
cout << "序列X、Y最长公共子序列为:" << endl;
LCS(M, N, x, c);
cout << endl;
}

void output(char *s, int n)
{
for (int i = 1; i <= n; i++)
{
cout << s[i] << " ";
}
cout << endl;
}

void LCSLength(int m, int n, char *x, char *y, int **c)
{
int i, j;

for (i = 1; i <= m; i++)
c[i][0] = 0;
for (i = 1; i <= n; i++)
c[0][i] = 0;

for (i = 1; i <= m; i++)
{
for (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];
}
}
}
}

void LCS(int i, int j, char *x, int **c)
{
if (i == 0 || j == 0)
{
system("pause");
return;
}
if (c[i][j] == c[i - 1][j - 1] + 1)
{
LCS(i - 1, j - 1, x, c);
cout << x[i] << " ";
}
else if (c[i - 1][j] >= c[i][j - 1])
{
LCS(i - 1, j, x, c);
}
else
{
LCS(i, j - 1, x, c);
}
}

原文地址:https://www.cnblogs.com/wxh-blos/p/12070586.html

时间: 2024-08-30 17:04:12

最大公共子序列(动规)的相关文章

[ACM] hdu 1231 最大连续子序列 (动规复习)

最大连续子序列 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 17687    Accepted Submission(s): 7828 Problem Description 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j

【线性动规】最大子段和

题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入输出格式 输入格式: 输入文件maxsum1.in的第一行是一个正整数N,表示了序列的长度. 第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列. 输出格式: 输入文件maxsum1.out仅包括1个整数,为最大的子段和是多少.子段的最小长度为1. 输入输出样例 输入样例#1: 7 2 -4 3 -1 2 -4 3 输出样例#1: 4 说明 [样例说明]2 -4 3 -1 2 -4 3 [数据规模与约定] 对于

LCS(最长公共子序列)动规算法正确性证明

今天在看代码源文件求diff的原理的时候看到了LCS算法.这个算法应该不陌生,动规的经典算法.具体算法做啥了我就不说了,不知道的可以直接看<算法导论>动态规划那一章.既然看到了就想回忆下,当想到算法正确性的时候,发现这个算法的正确性证明并不好做.于是想了一段时间,里面有几个细节很trick,容易陷进去.想了几轮,现在把证明贴出来,有异议的可以留言一起交流. 先把一些符号和约定说明下: 假设有两个数组,A和B.A[i]为A的第i个元素,A(i)为有A的第一个元素到第i个元素所组成的前缀.m(i,

0-1背包问题(动规基础,好吧虽然我现在在说大话,待续...)

(此位老兄的讲解深得我意,特来推荐:http://blog.csdn.net/insistgogo/article/details/8579597) 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. POINT: 1·每种物品仅有一件,可以选择放或不放. 2·子问题---将前i件物品放入容量为V的背包中.价值总和为f[i][V]; 若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵

算法&lt;初级&gt; - 第五章 递归与动规相关问题(完结)

算法<初级> - 第五章 递归与动规相关问题(完结) <一>递归和动态规划 暴力递归 转化为规模缩小了的同问题的子问题 - 时间复杂度O(2n-1) 有明确的边界条件(base case) - 先写base case,再写问题递归的过程 有得到子问题结果后决策过程 不记录每个子问题的解 - 每次求解子问题都交给递归去解决,不会在全局保存子问题的解(与动规形成对比) 动态规划DP 从暴力递归中延申 - 过程中还经历过<记忆化搜索>,相当于暴力递归+cache缓存(用has

POJ 2955 Brackets (动规)

Brackets Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2999   Accepted: 1536 Description We give the following inductive definition of a "regular brackets" sequence: the empty sequence is a regular brackets sequence, if s is a reg

sicily 1091 Maximum Sum (动规)

1 //1091.Maximum Sum 2 //b(i,j) = max{b(i,j-1)+a[j], max(b(i-1,t)+a[j])} (t<j) 3 #include <iostream> 4 using namespace std; 5 6 int main() { 7 int t; 8 cin>>t; 9 while (t--) { 10 int n; 11 cin>>n; 12 int a[n+1]; 13 for (int i = 1; i &

ACM/ICPC 之 经典动规(POJ1088-滑雪)

POJ1088-滑雪 将每个滑雪点都看作起点,从最低点开始逐个由四周递推出到达此点的最长路径的长度,由该点记下. 理论上,也可以将每一点都看作终点,由最高点开始计数,有兴趣可以试试. 1 //经典DP-由高向低海拔滑雪-求最长路 2 //Memory:372K Time:32 Ms 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<algorithm> 7 using

!HDU 1176--DP--(矩阵动规)

题意:有一个数轴,从0到10,小明开始在5这个位置.现在天上开始掉馅饼,小明每次只能移动单位一的长度,求小明最多能接到多少馅饼. 分析:刚开始接触动态规划,还没有真正理解动规的思维,所以刚开始的dp做法不知道对不对但是TLE了.正确的方法是建立一个以时间为行位置为列的矩阵,最初map[i][j]代表的是第i时刻j位置掉的馅饼的数量,状态转移方程:map[i][j]=map[i][j]+max(map[i+1][j-1],map[i+1][j],map[i+1][j+1]).也就是从最底层开始往上

HDU 1159 Common Subsequence 最大公共子序列

Problem Description A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, ..., xm> another sequence Z = <z1, z2, ..., zk> is a subsequence of X if there exists a stri