解题思路:
1.比较两个序列,寻找公共子序列,在这里要区分公共子序列和子串的区别:
子串是必须连续的,比如s1="abcde" s2="abc" s3="abde" 可以说s2是s1的子串,但是s3却 不是s1的子串
公共子序列是不必连续的,但是得满足位置要求,比如s1和s3的公共子序列为abde
2.搞明白这两者的差别,接下里分析如果有两个序列,现在每个序列都去掉最后一个元素,求新序列的最长公共子序列的话,结果并不会对原序列产生影响,原序列只是在新序列公共长度的基础上多加了一组元素,然后比对一下,如果这组元素相等,那么在前一个序列上加1,表示此时序列的公共长度加1,如果不相等,则考虑继承,所以满足最优子结构并且无后效性,利用动态规划来解
3.设置状态:利用dp【i】【j】来表示第一个序列(1-i)的长度和第二个序列(1-j)的长度的最长公共子序列。
4.状态转移:如果此时的a[i]==b[j]的话,那么dp[i][j]=dp[i-1][j-1]+1;即在(1-i-1)的长度和(1-j-1)的长度的基础上加1,因为又出现了一对相同的元素,否则的话,dp[i][j]=max(dp[i-1][j],dp[i][j-1])
4.初始化:很明显,当dp[0][j]都为0,因为第1个序列为空,当dp[i][0]也为0,因为第二个序列为空
5.接下来就是遍历填表,输出dp【i】【j】
#include<bits/stdc++.h>
using namespace std;
int a[1010],b[1010];//序列1和序列2
int dp[1010][1100];//dp数组
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];//输入序列1
for(int i=1;i<=n;i++)
cin>>b[i];//输入序列2
for(int i=0;i<=n;i++)
dp[0][i]=0;
for(int i=0;i<=n;i++)
dp[i][0]=0;//dp初始化(实际没必要)
for(int i=1;i<=n;i++)//枚举序列1的每一个元素
{
for(int j=1;j<=n;j++)//枚举序列2的每一个元素
{
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[n][n];//输出最长公共子序列
return 0;
}
算法优化:
1.不难看出,上述算法其实是在暴力枚举,时间复杂度为O(n*n),当数据为10^5时,超时。
2.题目中有一句关键信息为两个序列都是有1-n组成,即元素都相等,只是排列的方式不相等,如果把序列1看成一个本身就是单调递增的序列,那么求两个序列的公共子序列也就变成求序列2的最长上升子序列!
3.例如:题目中序列1: 3 2 1 4 5,如果改为a,b,c,d,e
即3-a,2-b,1-c,4-d,5-e,那么序列2(1,2,3,4,5)也就变成了c,b,a,d,e
序列2最长上升子序列长度为3(a,d,e)对应的数字是(3,4,5)即两个序列的LCS
4.那么如何变化呢?可以利用一个map数组和桶排序的方法,将每个数字变成的数字映射到数组中,即map[a[i]]=i;比如当i为1的时候 a[1]=3,map[a[1]]=1,意思是3这个数字变成了1。
5.然后将序列2也按照上述规则变化,利用贪心思想将dp数组的元素值设置成末尾最小值(参考最长上升子序列问题),这样,空间由N^2压缩为一维,时间由N^2压缩为n*logn
#include<bits/stdc++.h>
using namespace std;
int a[100010],b[100010],mapp[100010],dp[100010];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
mapp[a[i]]=i;//设置变化规则
}
for(int i=1;i<=n;i++)
cin>>b[i];
dp[1]=a[1];//dp初始化
int len=1;//记录最长上升子序列的长度
for(int i=2;i<=n;i++)
{
int low=0,high=len,mid;//设置二分查找的上下限
if(mapp[b[i]]>dp[len])//如果该数字比当前LIS的末位置大,说明又增加了一个长度
dp[++len]=mapp[b[i]];//加长并将这个数放入加长的位置
else
{
while(low<high)
{
mid=(low+high)/2;
if(dp[mid]>mapp[b[i]])
high=mid;
else
low=mid+1;
}
dp[low]=min(dp[low],mapp[b[i]]);
}
}
cout<<len;
return 0;
}