前言—子序列定义
在学习LIS和LCS之前,我们需要了解子序列的定义:假设我们定义一个长度为n的数组a[n],那么我们从数组中拿出m(不大于n)个元素,按照这些元素在原数组中排列的顺序排列成一个新的数组,我们可以说这个新的数组为原数组的子序列数组。
例如:a[5]={1,2,3,4,5},那么b[2]={1,3,5},c[3]={2,4,5}…都是a的子序列数组。在这里要强调,子序列数组中元素在原数组中不一定要求是连续的。例如不一定只有b[3]={1,2,3}或者c[3]={3,4,5}才是a的子序列数组,在这里特别指出。
在介绍完子序列的定义后,我们开始讲解LIS和LCS。
一、 LIS—最长上升子序列
**定义:**在一个长为n的数组a[n]中,我们需要找到这样一个子序列满足以下两个条件。
- 1.子序列的元素从左到右单调递增
- 2.在所有满足条件1的子序列中,找出长度最长的那个子序列
这样的子序列就是最长上升子序列。
DP解法:
由于本篇博客篇幅有限且重点在于动态规划,因此在这里介绍DP解法。在DP解法中,我们需要用到两个数组----原数组a[n]和动态规划数组dp[n]。详细思路让我们在代码里讲解。
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
typedef pair<int, int> PII;
int n;
int a[1050];
int dp[1050]; //dp[i]代表的是以a[i]结尾的(一定选上了a[i])最长上升子序列的长度。
int ma = 0; //ma存放最大值
void solve()
{
cin >> n; //n为数组长度
for (int i = 1; i <= n; i++)
{
dp[i] = 1;//首先,我们把整个dp数组初始化为1,因为长度为1的只含自己本身的子序列是一定满足条件的。
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j < i; j++)
{
if(a[j]<a[i])//对于每一个a[i],我们需要重新扫描a[i]到a[i-1],如果a[j]<a[i],看看a[i]加在以哪个数为终点的LIS后形成的新LIS最长
dp[i] = max(dp[i], dp[j] + 1);//dp数组需要不断更新以确保能获得最长长度
}
ma = max(dp[i], ma);//更新最后答案
}
cout << ma;
}
int main()
{
solve();
return 0;
}
时间复杂度O(n^2),并不是最优解。贪心加二分与树状数组维护的复杂度都更低,但是在这里不细说了。
二、 LCS—最长公共子序列
*定义:**在一个长为n的数组a[n]与长为m的子序列b[m]中,我们需要找到这样一个c[n]满足以下两个条件。
- 1.c[n]同时是a与b的子序列
- 2.在所有满足条件1的子序列中,找出长度最长的那个子序列
这样的子序列就是最长公共子序列。
DP解法:
我们定义一个dp[n][m],其中dp[i][j]表示子序列a[i]与b[j]的LCS的长度
而我们可以得到如下状态转移公式
- 1:if(i==0 并且 j==0) ---->dp[i][j]=0;
- 2:else if(a[i]==b[j])---->dp[i][j]==dp[i-1][j-1]+1;
- 3:else---->dp[i][j]==max(dp[i-1][j],dp[i][j-1]);
代码:
#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#define ll long long
#define fi first
#define se second
using namespace std;
typedef pair<int, int> PII;
string a, b;
int dp[1000][1000];
void solve()
{
while (cin >> a >> b)
{
int la = a.size(), lb = b.size();
memset(dp, 0, sizeof dp);
if (la == 0 || lb == 0)
{
cout << "0" << endl;
continue;
}
else
{
for (int i = 1; i <= la; i++)
{
for (int j = 1; j <= lb; j++)
{
if (a[i - 1] == b[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] =max(dp[i-1][j],dp[i][j-1]);
}
}
cout << dp[la][lb] << endl;
}
}
}
int main()
{
solve();
return 0;
}
时间复杂度为O(n*m)。
作者:Avalon·Demerzel,如果本篇博客有帮到你就留下一个赞吧。