二维dp
对于两个数组,我们就假设一个叫a,一个叫b吧,我们要求它们的最长公共子串,我们很容易想到的dp,我们可以设一个二维数组,,表示a数组的前i个和b数组的前j个的最长公共子串。我们假设存在i,j,使得,那么,dp[i][j]=dp[i-1][j-1]+1,如果,那么dp[i][j]=max(dp[i][j-1],dp[i-1][j]),这样做的时间复杂度原本是,但是我们想我们可以先预处理出来a数组中的数字在b数组中哪些位置出现了,我们这样做下来时间复杂度是均摊的,于是时间复杂度就是的,但是空间复杂度还是,于是在有些题中,每个数两两不同,而这种这么高的空间复杂度的做法是不能被接受的,所以我们就有了下面一种做法
一维dp
思路
还是原来的数组名,我们换一个思维,我们用一种加密的思想,把每个数换成跟它对应的数,我们这样做并不会影响我们的结果。我在前面说了,这种做法的前提是两两不同,于是我们就把,这样做不会影响结果。然后我们对b数组中的数也这么操作,如果发现它没有对应的,那它肯定没有价值,所以我们默认每个b数组中的值都在a数组中出现过,在这么操作完了之后,我们得到一个全新的b数组。我们发现a数组是递增的这不是TMD废话么?,那么我们求现在更新后的a数组和b数组的最长公共子串不就是求b数组的最长不下降序列,又要求两两不同,那不就是求b数组的最长上升序列么?(接下来出现的a、b数组皆为更新过的)我们设dp[i]为长度为i的子序列的最小的结尾,我们又设tot为现在最长上升子序列的长度,如果现在,那么,就是说我们在原先的最长上升子序列后面增添进,不然的话,我们可以二分出一个ans,使得,那么我们就要对进行更新,使,最后tot就是答案了
例题:P1439 【模板】最长公共子序列
代码
#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define Int register int
#define MAXN 100005
int n,tot;
int a[MAXN],b[MAXN],dp[MAXN];
map <int,int> DY;
int find (int x)
{
int l = 1,r = tot,ans = 0;
while (l <= r)
{
int mid = (l + r) >> 1;
if (dp[mid] > x) r = mid - 1;
else ans = mid,l = mid + 1;
}
return ans;
}
void read (int &x)
{
x = 0;char c = getchar();int f = 1;
while (c < '0' || c > '9') {if (c == '-') f = -f;c = getchar();}
while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + c - '0';c = getchar();}
x *= f;return ;
}
void write (int x)
{
if (x < 0){x = -x;putchar ('-');}
if (x > 9) write (x / 10);
putchar (x % 10 + '0');
}
signed main()
{
read (n);
for (Int i = 1;i <= n;++ i) read (a[i]),DY[a[i]] = i;
for (Int i = 1;i <= n;++ i) read (b[i]),b[i] = DY[b[i]];
for (Int i = 1;i <= n;++ i)
{
if (b[i] > dp[tot]) dp[++ tot] = b[i];
else
{
int where = find (b[i]);
dp[where + 1] = min(dp[where + 1],b[i]);
}
}
write (tot),putchar ('\n');
return 0;
}
来源:https://blog.csdn.net/nobody_like_you/article/details/99460120