LCS和LIS

LCS(最长公共子序列)

模板题:
http://acm.hdu.edu.cn/showproblem.php?pid=1159
Common Subsequence(最长公共子序列)
题意:
求两端字符串的最长公共子序列的长度

通过二维数组保存每段LCS的长度,则状态转移方程为:
LCS和LIS
实现过程:
以s1={1,3,4,5,6,7,7,8},s2={3,5,7,4,8,6,7,8,2}为例。
LCS和LIS
c[i,j]的定义,记录的LCS的长度值。
通过递归公式填入表中,如果横竖(i,j)对应的两个元素相等,该格子的值 = c[i-1,j-1] + 1。如果不等,取c[i-1,j] 和 c[i,j-1]的最大值。首先初始化该表:
LCS和LIS
S1的元素3 与 S2的元素3 相等,所以 c[2,1] = c[1,0] + 1。
LCS和LIS
S1的元素3 与 S2的元素5 不等,c[2,2] =max(c[1,2],c[2,1])。
LCS和LIS
中间填入规则不变,以此类推:
LCS和LIS
LCS和LIS
LCS和LIS
LCS和LIS

常规版本:
时间复杂度为O(n^2)
空间复杂度为O(n^2)
代码实现:

#include<bits\stdc++.h>
using namespace std;
int dp[2100][2100];
int main()
{
	string s,s1;
	while(cin>>s>>s1)
	{
		memset(dp,0,sizeof(dp));
		int a=s.size(),b=s1.size();
		for(int i=1;i<=a;i++)
		{
			for(int j=1;j<=b;j++)
			{
				if(s[i-1]==s1[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[a][b]<<endl;
	}
	return 0;
}

空间优化版本:
由于每一次的改变只与dp[i-1][j-1],dp[i][j-1]和dp[i-1][j]三个有关则可以通过滚动数组来保存数据,且数组基数最大为2,则可以选择模2。
时间复杂度为O(n^2)
空间复杂度为O(n)
代码实现:

#include<bits\stdc++.h>
using namespace std;
int dp[10][2100];
int main()
{
	string s,s1;
	while(cin>>s>>s1)
	{
		memset(dp,0,sizeof(dp));
		int a=s.size(),b=s1.size();
		for(int i=1;i<=a;i++)
		{
			for(int j=1;j<=b;j++)
			{
				if(s[i-1]==s1[j-1])
				{
					dp[i%2][j]=dp[(i-1)%2][j-1]+1;
				}
				else
				{
					dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1]);
				}
			}
		}
		cout<<dp[a%2][b]<<endl;
	}
	return 0;
}

LIS(最长上升子序列)

模板题:
https://vjudge.net/problem/POJ-2533

思路:
设dp[i]表示:从左至右扫描以ar[i]结尾的序列,就可以得到最长上升子序列。
dp(i)的递推公式如下:
1)dp(i)=1 当i=1;
2)dp(i)=max{dp(j)+1} 当ar[i]>ar[j],j大于等于1且小于i,i>1;
3)dp(i)=1 当对任意j,(j大于等于1且小于i),都有ar[i]<=ar[j];

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1100;
int dp[N],ar[N];
int main()
{
	int n,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&ar[i]);
	}
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
		dp[i]=1;
		for(int j=1;j<i;j++)
		{
			if(ar[i]>ar[j])
			{
				dp[i]=max(dp[i],dp[j]+1);
			}
		}
		ans=max(dp[i],ans);
	}
	cout<<ans<<endl;
	return 0;
}

优化练习:
https://www.luogu.com.cn/problem/AT2827(需要用DP加二分)不然要超时和爆空间

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e5+100;
int s[N],ar[N];
int main()
{
	int n,ans=0,tot=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&ar[i]);
	}
	memset(s,0,sizeof(s));
	s[++tot]=ar[1];
	for(int i=1;i<=n;i++)
	{
		if(ar[i]>s[tot])
		{
			s[++tot]=ar[i];
		}
		else
		{
			int temp=lower_bound(s+1,s+1+tot,ar[i])-s;
			s[temp]=ar[i];
		}
	}
	cout<<tot<<endl;
	return 0;
}

链接: LIS和LCS的题库.

上一篇:PTA L3 - 区区区间3 (30 分) (线段树、二分)


下一篇:7、小型企业无线网部署(案例3) AR充当AC的组网趣事~看看真机环境下会遇到什么问题