最长公共子串

戏子无情 提交于 2019-11-27 05:41:29

二维dp

对于两个数组,我们就假设一个叫a,一个叫b吧,我们要求它们的最长公共子串,我们很容易想到O(n2)O(n^2)的dp,我们可以设一个二维数组,dp[i][j]dp[i][j],表示a数组的前i个和b数组的前j个的最长公共子串。我们假设存在i,j,使得ai=bja_i=b_j,那么,dp[i][j]=dp[i-1][j-1]+1,如果ai!=bja_i!=b_j,那么dp[i][j]=max(dp[i][j-1],dp[i-1][j]),这样做的时间复杂度原本是O(n2)O(n^2),但是我们想我们可以先预处理出来a数组中的数字在b数组中哪些位置出现了,我们这样做下来时间复杂度是均摊的,于是时间复杂度就是O(n)O(n)的,但是空间复杂度还是O(n2)O(n^2),于是在有些题中,每个数两两不同,而这种这么高的空间复杂度的做法是不能被接受的,所以我们就有了下面一种做法

一维dp

思路

还是原来的数组名,我们换一个思维,我们用一种加密的思想,把每个数换成跟它对应的数,我们这样做并不会影响我们的结果。我在前面说了,这种做法的前提是两两不同,于是我们就把ai>ia_i->i,这样做不会影响结果。然后我们对b数组中的数也这么操作,如果发现它没有对应的,那它肯定没有价值,所以我们默认每个b数组中的值都在a数组中出现过,在这么操作完了之后,我们得到一个全新的b数组。我们发现a数组是递增的这不是TMD废话么?,那么我们求现在更新后的a数组和b数组的最长公共子串不就是求b数组的最长不下降序列,又要求两两不同,那不就是求b数组的最长上升序列么?(接下来出现的a、b数组皆为更新过的)我们设dp[i]为长度为i的子序列的最小的结尾,我们又设tot为现在最长上升子序列的长度,如果现在bi>dp[tot]b_i>dp[tot],那么dp[++tot]=bidp[++tot]=b_i,就是说我们在原先的最长上升子序列后面增添进bib_i,不然的话,我们可以二分出一个ans,使得dp[ans]<=bidp[ans+1]>bidp[ans]<=b_i且dp[ans+1]>b_i,那么我们就要对dp[ans]dp[ans]进行更新,使dp[ans]=min(dp[ans],bi)dp[ans]=min(dp[ans],b_i),最后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;
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!