最长公共子序列

题目描述

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列 X=<x1,x2,…,xm>,则另一序列 Z<z1z2zk> 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<x1x2xm> Y=<y1,y2…yn>.要求找出 X  Y 的一个最长公共子序列。

输入格式

输入文件共有两行。每行为一个由大写字母构成的长度不超过 200 的字符 串,表示序列 X Y

输出格式

输出文件第一行为一个非负整数。表示所求得的最长公共子序列的长度。 若不存在公共子序列.则输出文件仅有一行输出一个整数 0

输入样例

ABCBDAB

BDCABA

输出样例

4

首先想到的就是线性动规,最重要的就是找到动态转移方程。那么如何推出状态方程那么就是画图了,从图中可以更好的了解每个dp数组的变化。

bBDCABA
adp000000
A0000000
B0000000
C0000000
B0000000
D0000000
A0000000
B0000000

刚开始dp数组全部初始化为0,然后开始比较a和b的每个元素,首先看到b的第一个字符‘B’

当a出现‘B’后那么后边的dp数组就全部加一

bBDCABA
adp000000
A0000000
B0111111
C0100000
B0100000
D0100000
A0100000
B0100000

依此类推

因为看的是b所以计数是a数组

bBDCABA
adp000000
A0000000
B0111111
C0111111
B0111111
D0122222
A0120000
B0120000
bBDCABA
adp000000
A0000000
B0111111
C0112222
B0112222
D0122222
A0123333
B0123444

有很多人应该看不懂画的意思,其实就是当遇到相同的数字后,那么就让这个dp数组在a和b的上一个dp上加一(dp[i - 1][j - 1] + 1),假如不同的话就从它的上一个字母找出最大的数字,将值赋给它及(max(dp[i - 1][j], dp[i][j - 1])),然后状态方程就出来了:

for(int i = 1; i < len1; i++)
	{
		for(int j = 1; j < len2; j++)
		{
			if(a[i] == b[j])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}else
			{
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
	}

       

代码如下:

#include<bits/stdc++.h>
using namespace std;
int dp[205][205];
int main() 
{
	string a, b;
	cin >> a >> b;
	a = '0' + a;
	b = '0' + b;
	int len1 = a.size();
	int len2 = b.size();
	for(int i = 1; i < len1; i++)
	{
		for(int j = 1; j < len2; j++)
		{
			if(a[i] == b[j])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}else
			{
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
	}
	cout << dp[len1 - 1][len2 - 1] << endl;
	return 0;
}

为什么要加字符‘0’呢,就是为了让第一个字符可以得到计算,如果没有那么假如第一个字符相同那么就会出错。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值